Elixir v1.20: jetzt eine Sprache mit schrittweiser Typisierung
(elixir-lang.org)- Typinferenz und schrittweise (gradual) Typprüfung gelten für alle Elixir-Programme und finden auch ohne Typannotationen toten Code sowie nachgewiesene Bugs, die zur Laufzeit zwangsläufig fehlschlagen
- Der Typ
dynamic()verfolgt im Unterschied zuany(), das „alles erlaubt“, den zur Laufzeit möglichen Typbereich und meldet nur dann eine Verletzung, wenn es keinerlei vollständige Überschneidung mit den erlaubten Typen gibt - Ein Wert vom Typ
dynamic(integer() or binary())erzeugt bei Aufrufen mit teilweise überlappenden Möglichkeiten, etwa Zahlenoperationen oder String-Funktionen, keine Verletzung, wohl aber bei Aufrufen wieMap.fetch!, die nurmapakzeptieren dynamic()wird je nach Verwendungsweise weiter eingegrenzt und verfeinert in Code wiedata.a + data.bdatazu einermapder Form%{..., a: number(), b: number()}- In Guards werden Vereinigung, Schnittmenge und Negation inferiert, sodass Bedingungen wie
is_list,is_integer,is_map_key,not is_map_keyundtuple_sizeals Typinformationen genutzt werden case- und Bedingungsausdrücke übernehmen Informationen aus vorangehenden Klauseln in die folgenden Klauseln und führen so Typprüfungen durch, bei denen etwa zuerstnilbehandelt und der verbleibende Wert anschließend aufbinary()eingegrenzt wird- Mehrere tuple- und map-bezogene Funktionen der Standardbibliothek wurden mit Typen versehen, was in bestehenden Codebasen dabei hilft, redundante Klauseln und toten Code zu finden
- Im „If T: Benchmark for Type Narrowing” wurden 12 von 13 Kategorien bestanden, was zeigt, dass sich in gewöhnlichem Elixir-Code präzise Typinformationen wiederherstellen lassen
- v1.20 verbessert zudem erneut die Kompilierzeit von Anwendungen in Multi-Core-Umgebungen, und in synthetischen Benchmarks erzielte das Elixir-Build-Tool das schnellste Ergebnis unter den BEAM-Sprachen
- Die neue Compiler-Option
:module_definitionerlaubt es, die Ausführungsweise von Moduldefinitionen zwischen dem Standard:compiledund:interpretedzu wählen; aktiviert wird sie mitelixirc_options: [module_definition: :interpreted]inmix.exs - Die Option
:module_definitionwirkt sich nicht auf die auf die Festplatte geschriebenen.beam-Dateien aus, sondern ändert nur die Ausführungsweise innerhalb vondefmodule; das kann zur Verbesserung der Kompilierzeit in großen Projekten beitragen - Neue Typsignaturen auf Basis mengentheoretischer Typen sollen nach weiterer Forschung zu der Leistung des Typsystems in v1.20, rekursiven Typen, parametrierten Typen und der Iteration über map-Schlüssel-Wert-Paare zusammen mit Definitionen für typed structs diskutiert werden
1 Kommentare
Meinungen auf Hacker News
Das ist vielleicht nur meine persönliche Erfahrung, aber bei Sprachen, die nicht von Anfang an Typen hatten, fühlt es sich so an, als würden sie nie so gut funktionieren wie wirklich statisch typisierte Sprachen
Ich arbeite seit etwa 10 Jahren als professioneller Elixir-Entwickler und habe schon lange darauf gewartet, dass Typen kommen. Deshalb freue ich mich sehr, dass damit jetzt tatsächlich begonnen wurde
Ich würde aber gern wissen, wie sich der Stand in v1.20 im Vergleich zu Dialyzer ohne Spezifikationen verhält. Ich hatte den Success-Typing-Ansatz von Dialyzer so verstanden, dass er eher bedeutet „nicht warnen, wenn es auch nur eine funktionierende Argumentkombination gibt“ statt „warnen, wenn es eine Argumentkombination gibt, die fehlschlagen kann“, und ich dachte, was Elixir hier macht, sei ähnlich; Dialyzer fand ich insgesamt nicht besonders nützlich
Ich habe auf HN schon ein paar Beiträge über das graduelle Typsystem von Elixir gesehen, bin den Details aber nicht gefolgt. Mich würde interessieren, ob jemand weiß, ob dieses graduelle Typsystem die asymptotische Komplexität eines Programms im Vergleich zu untypisiertem Code verändern kann
Soweit ich weiß, können die meisten graduellen Typsysteme, zum Beispiel Racket, Programme asymptotisch langsamer machen, auch wenn es einige Ausnahmen gibt [1]
[1] https://doi.org/10.1145/3314221.3314627
Die meisten graduellen Typsysteme fügen Zwangsprüfungen ein, wenn Werte die Grenze zwischen typisiertem und untypisiertem Code überschreiten. Zum Beispiel werden alle Elemente einer Liste geprüft oder Werte in Typ-Proxys verpackt. Das Elixir-Team hat jedoch strong arrows-Ergebnisse vorgestellt, um Soundness ohne solche Laufzeitprüfungen zu erreichen, und der vom Compiler erzeugte Bytecode ist semantisch identisch mit untypisiertem Code
Ironischerweise sagten die Kritiker, dass Typen nötig seien, und Elixir-Fans sagten, Typen seien nicht nötig und Elixir sei irgendwie magisch, sodass keine typenbezogenen Bugs aufträten. Jetzt, wo Typen eingebaut werden, finden sie tatsächlich Bugs. Waren sie zur Fehlervermeidung also doch nicht unnötig? Trotzdem ist das eine gute Entwicklung. Ich habe Elixir früher ziemlich viel benutzt und mochte es, aber dem Fehlen von Typen konnte ich nur schwer zustimmen
https://en.wiktionary.org/wiki/Goomba_fallacy
Ich stimme dieser Sicht nicht zu, aber sie ist deutlich besser zu verteidigen als „$LANGUAGE ist Magie“
Vor den Fortschritten in der mengenbasierten Typentheorie könnte diese Position sogar richtig gewesen sein
Und irgendwann werden dann doch statische Typen hinzugefügt. Das ist bei Python, JavaScript und Ruby passiert, und es gibt sicher noch mehr Beispiele
Ich finde es großartig, dass ich Elixir aktualisieren kann, ohne dass es in mehreren Projekten Breaking Changes gibt, und dass der Compiler gratis Bugs findet. Daran habe ich mich schon viel zu sehr gewöhnt
Das freut mich wirklich. Es kommt jetzt immer näher an eine „großartige Sprache“ heran, und für mich ist Elixir der erste Kandidat
Falls jemand eine andere Sprache kennt, die sich schon jetzt angenehm benutzen lässt und trotzdem weiterhin hervorragende Funktionen stabil und sicher hinzufügt, wäre ich für Hinweise dankbar. Ich hatte erst Go intensiv gelernt und bin dann zu fortgeschrittenem C# gewechselt, weil es sich bei Go so anfühlte, als würden keine guten Funktionen mehr hinzukommen
Ich habe im letzten Monat den Elixir-Track auf exercism.io gemacht https://exercism.org/tracks/elixir
Wirklich großartig
Ach ja, jetzt geht’s wieder los. Sieht so aus, als würde ich noch mal ein Jahr lang Elixir lernen.
Ich mag eigentlich alles an Elixir, aber von allen Sprachen bringt mich Elixir am ehesten dazu, ständig an mir selbst zu zweifeln. Mein Gehirn scheint nicht so recht für funktionale Programmierung gemacht zu sein, aber durch diese Änderung bekomme ich wieder Lust, es noch einmal zu versuchen.
Schade ist nur, dass das Ökosystem nicht gerade anfängerfreundlich wirkt und bei Antworten auf Fragen oft vorausgesetzt wird, dass man die Sprache schon ziemlich gut kennt.
Lass dich nicht vom Titel täuschen. Die erste Hälfte des Buchs ist einfach nur Elixir.
In den letzten 8 Jahren habe ich jedes Mal, wenn ich wieder in Elixir reinkommen wollte, dieses Buch genommen, und es hat jedes Mal gut funktioniert. Ich habe es allerdings nie ganz zu Ende gelesen.
Eines meiner Kriterien für gute Programmierbücher dieser Tutorial-Projekt-Art ist, ob sie mir schon ungefähr zur Hälfte genug Werkzeuge geben, um an meine eigentliche Arbeit zu gehen, selbst wenn ich sie mehrmals angefangen, aber nie beendet habe.
Aber ich glaube nicht, dass es daran liegt, dass das Gehirn nicht dafür gemacht ist, sondern eher am Kontrast zwischen dem Erfahrungsstand, den man in imperativen Sprachen aufgebaut hat, und der Tatsache, dass man im rein funktionalen Stil wieder als Anfänger anfängt.
Es wird mit der Zeit besser. Mit funktionaler Programmierung bin ich warm geworden, als mir klar wurde, wie sehr ich es mag, locker formatierte Bash-„One-Liner“ zusammenzusetzen. Man schaut, in welcher Form die Daten vorliegen, kippt sie mit einem Befehl raus, überlegt sich den nächsten Schritt, der sie näher an die gewünschte Form bringt, piped sie in den nächsten Befehl und schaut wieder drauf. Wenn man so weitermacht, bleibt am Ende meistens eine Folge von Datentransformationen übrig, bei denen die Daten selbst nicht verändert werden.
Dass sich das in der Shell angenehm anfühlt, liegt auch daran, dass man sich beim täglichen Herumwandern im Dateisystem ständig mehr Befehlsvokabular aneignet. Die vertraute Bibliothek von „Funktionen“ in einer Unix-artigen Umgebung ist über Jahre ziemlich groß geworden. In einer rein funktionalen Programmierumgebung muss man im Grunde dasselbe tun, nur kostet das Lernen dieses Vokabulars etwas mehr Mühe. Die häufig benutzten „Befehle“ heißen dann nicht grep, cat oder sort, sondern Funktionen wie map, fold oder zip.
Im Kern ist es aber wirklich dasselbe, und die Faszination des Pipeline-Bauens gilt auf beiden Seiten gleichermaßen. Man kann es Stück für Stück aufbauen, und bei jedem Teilstück den vorherigen Schritt vergessen und nur darüber nachdenken, wie sich die Daten vor einem als Nächstes transformieren lassen. Diese Kontextarmut ist erfrischend und angenehm.
Probier es unbedingt aus und hab hoffentlich Spaß daran. Wenn man lernt, es auszuhalten, etwas nicht zu können, wird man am Ende wirklich gut darin.
Klar, ich habe nur ein paar Nachmittage damit verbracht, aber wenn ich mein Gehirn wieder auf funktionale Sprachen trainieren wollte, würde ich allein wegen der Vertrautheit wohl Gleam wählen.
Manchmal bleiben Beiträge unbeachtet, weil sie zu vage sind oder nach „macht bitte meine Hausaufgaben“ riechen.
Aber Beiträge mit echter Neugier bekommen meiner Erfahrung nach immer Antworten.
Großartig. In 1.20 scheint die Kompilierung unserer großen Umbrella-App deutlich schneller geworden zu sein.
Ich frage mich, wie das im Vergleich zu Gleam aussieht. Oder anders: Warum sollte man jetzt noch Elixir statt Gleam verwenden? Phoenix und besonders LiveView scheinen ein großer Reiz von Elixir zu sein.
Wie der aktuelle Stand von Gleam OTP ist, weiß ich nicht, aber als ich zuletzt geschaut habe, sah es nicht gut aus.
Wenn dir beides egal ist und dich nur Typen interessieren, dann nimm Gleam. Aber dann könnte man doch auch einfach Rust nehmen, oder?
Zum Beispiel ist JSON-Decoding/-Encoding in Gleam eher umständlich. In Rust leitet man einfach serde ab, und in Elixir reicht ein Funktionsaufruf.
Elixir hat das reifere Ökosystem. Man kann in Gleam zwar Phoenix oder andere Gleam-Frameworks nutzen, aber die Erfahrung ist nicht dieselbe.
Ein großer Grund, warum Gleam reizvoller ist als Elixir, sind die Typen, und Elixir schließt diese Lücke jetzt. Dazu kommt, dass es nach JavaScript kompilieren kann; in Elixir macht Hologram etwas Ähnliches.
Persönlich mag ich das Typsystem von Gleam und die Rust-artige Syntax lieber, aber im Moment fühlt sich Elixir für all meine Webentwicklungsprojekte nach der besseren Wahl an.