1 Punkte von GN⁺ 3 시간 전 | 1 Kommentare | Auf WhatsApp teilen
  • 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 zu any(), 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 wie Map.fetch!, die nur map akzeptieren
  • dynamic() wird je nach Verwendungsweise weiter eingegrenzt und verfeinert in Code wie data.a + data.b data zu einer map der 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_key und tuple_size als 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 zuerst nil behandelt und der verbleibende Wert anschließend auf binary() 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_definition erlaubt es, die Ausführungsweise von Moduldefinitionen zwischen dem Standard :compiled und :interpreted zu wählen; aktiviert wird sie mit elixirc_options: [module_definition: :interpreted] in mix.exs
  • Die Option :module_definition wirkt sich nicht auf die auf die Festplatte geschriebenen .beam-Dateien aus, sondern ändert nur die Ausführungsweise innerhalb von defmodule; 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

 
GN⁺ 3 시간 전
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

    • Stimme zu, aber es gibt auch eine andere Perspektive: „Why are gradual static types so great?“ https://www.benkuhn.net/gradual/
    • Andererseits ist TypeScript zu meinem liebsten Typsystem geworden, weil es all die seltsamen Muster unterstützen muss, die Menschen in einer untypisierten Sprache verwendet haben
  • 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 bin gespannt, was es in einer noch produktiv genutzten, 10 Jahre alten Elixir-Codebasis finden wird
  • 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

    • Das graduelle Typsystem von Elixir kann die asymptotische Komplexität eines Programms nicht verändern. Es schließt Laufzeit-Casts an statisch/dynamischen Grenzen, die in anderen graduellen Typsystemen die Verlangsamung verursachen, explizit aus
      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

    • Das ist die Goomba fallacy
      https://en.wiktionary.org/wiki/Goomba_fallacy
    • Ich habe Elixir nicht besonders tief verfolgt, aber die Diskussionen auf der Erlang-Seite, die ich gesehen habe, klangen etwas anders. Die Idee war, dass man Fehler ohnehin elegant behandeln müsse und Typfehler deshalb einfach über die ohnehin vorhandenen Fehlerbehebungsmechanismen behandeln könne
      Ich stimme dieser Sicht nicht zu, aber sie ist deutlich besser zu verteidigen als „$LANGUAGE ist Magie“
    • Ich kann nicht garantieren, dass ich so eine Behauptung nie gesehen habe, aber ich erinnere mich nicht daran, und selbst wenn es sie gab, war das wohl nur eine winzige Minderheit. Das eigentliche Gegenargument war meistens eher: „Typen sind gut, aber sie haben Kosten, und diese Kosten bringen nicht immer genug Nutzen zurück“
      Vor den Fortschritten in der mengenbasierten Typentheorie könnte diese Position sogar richtig gewesen sein
    • Dasselbe ist auch bei JavaScript/TypeScript und Python passiert. Manchmal kann man Leute nur so denken lassen, wie sie eben denken wollen
    • So ist der Lauf der Dinge. Dynamisch typisierte Sprachen gewinnen Fans, und andere sagen völlig zu Recht, dass sie mit statischen Typen viel nützlicher wären. Die Fans nehmen das persönlich und sagen, statische Typen seien nicht nötig. Die Gründe variieren: „sowieso nicht nützlich“, „widerspricht dem Geist der Sprache“, „ist doch nur eine Skriptsprache“, „benutz einfach einen Debugger“, „statische Typen schaden der Produktivität“ und so weiter
      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.

    • https://pragprog.com/titles/lhelph/functional-web-developmen...
      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.
    • Ich habe das zum ersten Mal an der Uni erlebt, als ich in einem Kurs wie „Überblick über Programmierparadigmen“ zum ersten Mal Haskell gemacht habe, und es war wirklich schmerzhaft. Ich hatte damals schon ein paar Jahre programmiert und konnte kaum glauben, wie hilflos ich mich dabei fühlte, Dinge zu Ende zu bringen, die sich lange völlig grundlegend angefühlt hatten.
      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.
    • Ich frage mich, ob du ein bisschen Rust kannst. Ich habe selbst nicht besonders viel Erfahrung mit funktionalen Sprachen, aber Gleam fühlte sich wegen seiner Rust-artigen Seiten vertraut an, sodass ich mich mehr auf die Konzepte als auf die Syntax konzentrieren konnte.
      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.
    • Ich würde empfehlen, auf ElixirForum zu fragen. Wirklich feindselige Reaktionen habe ich dort noch nie gesehen.
      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.
    • Solche Aussagen verwirren mich immer. Ein mit Zustand vollgestopftes objektorientiertes Programm ist für mich viel schwerer zu verstehen.
  • 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.

    • Es ist im Grunde die Frage, ob man Rust mag oder Erlang. Gleam zu verwenden fühlt sich an wie Rust zu verwenden, Elixir zu verwenden fühlt sich an wie Erlang zu verwenden.
      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?
    • Auf der Gleam-Website gibt es direkt Vergleichsmaterial.
    • Gleam hat keine Makros, und viele Elixir-Bibliotheken wie Phoenix oder Ecto setzen Makros sehr effektiv ein.
      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.
    • Praktisch gesehen sind es Phoenix und Ecto.