Erste Schritte mit der Programmiersprache Elm
Veröffentlicht: 2022-03-11Als der leitende Entwickler eines sehr interessanten und innovativen Projekts vorschlug, von AngularJS zu Elm zu wechseln, war mein erster Gedanke: Warum?
Wir hatten bereits eine gut geschriebene AngularJS-Anwendung, die sich in einem soliden Zustand befand, gut getestet und in der Produktion bewährt war. Und Angular 4, das ein würdiges Upgrade von AngularJS ist, hätte eine natürliche Wahl für die Neufassung sein können – ebenso wie React oder Vue. Elm schien eine seltsame domänenspezifische Sprache zu sein, von der die Leute kaum gehört haben.
Nun, das war, bevor ich etwas über Elm wusste. Jetzt, mit etwas Erfahrung damit, insbesondere nach dem Wechsel von AngularJS, denke ich, dass ich eine Antwort auf dieses „Warum“ geben kann.
In diesem Artikel gehen wir auf die Vor- und Nachteile von Elm ein und erklären, wie einige seiner exotischen Konzepte perfekt zu den Anforderungen eines Front-End-Webentwicklers passen. Einen eher Tutorial-ähnlichen Elm-Sprachartikel finden Sie in diesem Blogbeitrag.
Elm: Eine rein funktionale Programmiersprache
Wenn Sie es gewohnt sind, in Java oder JavaScript zu programmieren, und das Gefühl haben, dass dies die natürliche Art ist, Code zu schreiben, kann das Erlernen von Elm wie ein Sturz in den Kaninchenbau sein.
Das erste, was Ihnen auffallen wird, ist die seltsame Syntax: keine geschweiften Klammern, viele Pfeile und Dreiecke.
Sie lernen vielleicht, ohne geschweifte Klammern zu leben, aber wie definieren und verschachteln Sie Codeblöcke? Oder wo ist die for
-Schleife oder eine andere Schleife? Mit einem let
-Block kann zwar ein expliziter Gültigkeitsbereich definiert werden, es gibt jedoch keine Blöcke im klassischen Sinne und überhaupt keine Schleifen.
Elm ist eine rein funktionale, stark typisierte, reaktive und ereignisgesteuerte Web-Client-Sprache.
Sie fragen sich vielleicht, ob die Programmierung auf diese Weise überhaupt möglich ist.
In Wirklichkeit summieren sich diese Qualitäten zu einem erstaunlichen Paradigma der Programmierung und Entwicklung guter Software.
Rein funktional
Sie könnten denken, dass Sie mit der neueren Version von Java oder ECMAScript 6 funktional programmieren können. Aber das ist nur die Oberfläche.
In diesen Programmiersprachen haben Sie immer noch Zugriff auf ein Arsenal an Sprachkonstrukten und die Versuchung, auf die nicht funktionalen Teile davon zurückzugreifen. Wo Sie den Unterschied wirklich bemerken, ist, wenn Sie nichts anderes tun können als funktionale Programmierung. An diesem Punkt beginnt man schließlich zu spüren, wie natürlich funktionale Programmierung sein kann.
In Elm ist fast alles eine Funktion. Der Datensatzname ist eine Funktion, der Vereinigungstypwert ist eine Funktion – jede Funktion besteht aus teilweise angewendeten Funktionen auf ihre Argumente. Sogar die Operatoren wie Plus (+) und Minus (-) sind Funktionen.
Um eine Programmiersprache als rein funktional zu deklarieren, ist nicht die Existenz solcher Konstrukte, sondern das Fehlen von allem anderen von größter Bedeutung. Erst dann kann man rein funktional denken.
Elm basiert auf ausgereiften Konzepten der funktionalen Programmierung und ähnelt anderen funktionalen Sprachen wie Haskell und OCaml.
Stark typisiert
Wenn Sie in Java oder TypeScript programmieren, wissen Sie, was das bedeutet. Jede Variable muss genau einen Typ haben.
Natürlich gibt es einige Unterschiede. Wie bei TypeScript ist die Typdeklaration optional. Wenn nicht vorhanden, wird davon ausgegangen. Aber es gibt keinen „beliebigen“ Typ.
Java unterstützt generische Typen, aber auf eine bessere Art und Weise. Generics in Java wurden später hinzugefügt, daher sind Typen nicht generisch, sofern nicht anders angegeben. Und um sie zu verwenden, brauchen wir die hässliche <>
Syntax.
In Elm sind Typen generisch, sofern nicht anders angegeben. Schauen wir uns ein Beispiel an. Angenommen, wir brauchen eine Methode, die eine Liste eines bestimmten Typs nimmt und eine Zahl zurückgibt. In Java wäre es:
public static <T> int numFromList(List<T> list){ return list.size(); }
Und in der Elm-Sprache:
numFromList list = List.length list
Obwohl optional, empfehle ich dringend, dass Sie immer Typdeklarationen hinzufügen. Der Elm-Compiler würde niemals Operationen mit falschen Typen zulassen. Für einen Menschen ist es viel einfacher, einen solchen Fehler zu machen, besonders beim Erlernen der Sprache. Das obige Programm mit Typanmerkungen wäre also:
numFromList: List a -> Int numFromList list = List.length list
Es kann zunächst ungewöhnlich erscheinen, die Typen in einer separaten Zeile zu deklarieren, aber nach einiger Zeit sieht es natürlich aus.
Web-Client-Sprache
Das bedeutet einfach, dass Elm in JavaScript kompiliert wird, sodass Browser es auf einer Webseite ausführen können.
Angesichts dessen ist es keine Allzwecksprache wie Java oder JavaScript mit Node.js, sondern eine domänenspezifische Sprache zum Schreiben des Client-Teils von Webanwendungen. Darüber hinaus beinhaltet Elm sowohl das Schreiben der Geschäftslogik (was JavaScript macht) als auch den Präsentationsteil (was HTML macht) – alles in einer funktionalen Sprache.
All dies geschieht auf eine sehr spezifische rahmenartige Weise, die als The Elm Architecture bezeichnet wird.
Reaktiv
Die Elm-Architektur ist ein reaktives Web-Framework. Alle Änderungen an den Modellen werden ohne explizite DOM-Manipulation sofort auf der Seite gerendert.
In dieser Hinsicht ähnelt es Angular oder React. Aber Elm tut es auch auf seine eigene Art und Weise. Der Schlüssel zum Verständnis der Grundlagen liegt in der Signatur der view
und update
:
view : Model -> Html Msg update : Msg -> Model -> Model
Eine Elm-Ansicht ist nicht nur die HTML-Ansicht des Modells. Es ist HTML, das Nachrichten der Art Msg
erzeugen kann, wobei Msg
ein exakter Vereinigungstyp ist, den Sie definieren.
Jedes standardmäßige Seitenereignis kann eine Nachricht erzeugen. Und wenn eine Nachricht erstellt wird, ruft Elm intern die Update-Funktion mit dieser Nachricht auf, die dann das Modell basierend auf der Nachricht und dem aktuellen Modell aktualisiert, und das aktualisierte Modell wird erneut intern für die Ansicht gerendert.
Ereignisgesteuert
Ähnlich wie JavaScript ist Elm ereignisgesteuert. Aber anders als beispielsweise bei Node.js, wo einzelne Rückrufe für die asynchronen Aktionen bereitgestellt werden, werden Elm-Ereignisse in diskreten Sätzen von Nachrichten gruppiert, die in einem Nachrichtentyp definiert sind. Und wie bei jedem Union-Typ können die Informationen, die separate Typwerte tragen, alles sein.
Es gibt drei Ereignisquellen, die Nachrichten erzeugen können: Benutzeraktionen in der Html
Ansicht, Ausführung von Befehlen und äußere Ereignisse, die wir abonniert haben. Aus diesem Grund enthalten alle drei Typen Html
, Cmd
und Sub
msg
als Argument. Und der generische msg
-Typ muss in allen drei Definitionen gleich sein – derselbe, der an die Aktualisierungsfunktion übergeben wird (im vorherigen Beispiel war es der Msg
-Typ mit einem großen M), bei dem die gesamte Nachrichtenverarbeitung zentralisiert ist.
Quellcode eines realistischen Beispiels
Ein vollständiges Elm-Webanwendungsbeispiel finden Sie in diesem GitHub-Repository. Obwohl es einfach ist, zeigt es die meisten Funktionen, die in der täglichen Clientprogrammierung verwendet werden: Abrufen von Daten von einem REST-Endpunkt, Decodieren und Codieren von JSON-Daten, Verwenden von Ansichten, Nachrichten und anderen Strukturen, Kommunizieren mit JavaScript und alles, was zum Kompilieren und Packen erforderlich ist Elm-Code mit Webpack.
Die Anwendung zeigt eine Liste von Benutzern an, die von einem Server abgerufen wurden.
Für einen einfacheren Einrichtungs-/Demo-Prozess wird der Dev-Server von Webpack sowohl für das Packen von allem, einschließlich Elm, als auch für das Bereitstellen der Benutzerliste verwendet.
Einige Funktionen befinden sich in Elm und einige in JavaScript. Dies geschieht absichtlich aus einem wichtigen Grund: Um die Interoperabilität zu zeigen. Wahrscheinlich möchten Sie Elm ausprobieren, um zu starten, oder nach und nach vorhandenen JavaScript-Code darauf umstellen oder neue Funktionen in der Elm-Sprache hinzufügen. Dank der Interoperabilität funktioniert Ihre App weiterhin sowohl mit Elm- als auch mit JavaScript-Code. Dies ist wahrscheinlich ein besserer Ansatz, als die gesamte App in Elm von Grund auf neu zu starten.
Der Elm-Teil im Beispielcode wird zuerst mit Konfigurationsdaten aus JavaScript initialisiert, dann wird die Liste der Benutzer abgerufen und in der Elm-Sprache angezeigt. Nehmen wir an, wir haben bereits einige Benutzeraktionen in JavaScript implementiert, sodass beim Aufrufen einer Benutzeraktion in Elm nur der Rückruf an sie gesendet wird.
Der Code verwendet auch einige der Konzepte und Techniken, die im nächsten Abschnitt erläutert werden.
Anwendung von Ulmenkonzepten
Lassen Sie uns einige der exotischen Konzepte der Programmiersprache Elm in realen Szenarien durchgehen.
Unionstyp
Das ist das reine Gold der Elm-Sprache. Erinnern Sie sich an all die Situationen, in denen strukturell unterschiedliche Daten mit demselben Algorithmus verwendet werden mussten? Es ist immer schwierig, solche Situationen zu modellieren.
Hier ist ein Beispiel: Stellen Sie sich vor, Sie erstellen eine Paginierung für Ihre Liste. Am Ende jeder Seite sollten Links zu vorherigen, nächsten und allen Seiten mit ihren Nummern stehen. Wie strukturieren Sie es, um die Informationen darüber zu speichern, auf welchen Link der Benutzer geklickt hat?
Wir können mehrere Rückrufe für vorherige, nächste und Seitenzahl-Klicks verwenden, oder wir können ein oder zwei boolesche Felder verwenden, um anzugeben, was angeklickt wurde, oder bestimmten ganzzahligen Werten wie negativen Zahlen, Null usw. eine besondere Bedeutung geben. Aber keines davon Diese Lösungen können genau diese Art von Benutzerereignissen modellieren.
In Elm ist es ganz einfach. Wir würden einen Union-Typ definieren:
type NextPage = Prev | Next | ExactPage Int
Und wir verwenden es als Parameter für eine der Nachrichten:
type Msg = ... | ChangePage NextPage
Schließlich aktualisieren wir die Funktion, um einen case
zu haben, um den Typ von nextPage
zu überprüfen:
update msg model = case msg of ChangePage nextPage -> case nextPage of Prev -> ... Next -> ... ExactPage newPage -> ...
Das macht die Sache sehr elegant.
Erstellen mehrerer Kartenfunktionen mit <|
Viele Module enthalten eine map
mit mehreren Varianten, die auf eine unterschiedliche Anzahl von Argumenten angewendet werden können. Zum Beispiel hat List
map
, map2
, … , bis map5
. Aber was ist, wenn wir eine Funktion haben, die sechs Argumente akzeptiert? Es gibt keine map6
. Aber es gibt eine Technik, um das zu überwinden. Es verwendet das <|
Funktion als Parameter und partielle Funktionen, wobei einige der Argumente als Zwischenergebnisse angewendet werden.
Nehmen wir der Einfachheit halber an, dass eine List
nur map
und map2
, und wir möchten eine Funktion anwenden, die drei Argumente auf drei Listen akzeptiert.
So sieht die Umsetzung aus:
map3 foo list1 list2 list3 = let partialResult = List.map2 foo list1 list2 in List.map2 (<|) partialResult list3
Angenommen, wir möchten foo
verwenden, das einfach seine numerischen Argumente multipliziert, die wie folgt definiert sind:
foo abc = a * b * c
Das Ergebnis von map3 foo [1,2,3,4,5] [1,2,3,4,5] [1,2,3,4,5]
ist also [1,8,27,64,125] : List number
.
Lassen Sie uns dekonstruieren, was hier vor sich geht.
Zunächst wird in partialResult = List.map2 foo list1 list2
foo
teilweise auf jedes Paar in list1
und list2
. Das Ergebnis ist [foo 1 1, foo 2 2, foo 3 3, foo 4 4, foo 5 5]
, eine Liste von Funktionen, die einen Parameter akzeptiert (da die ersten beiden bereits angewendet werden) und eine Zahl zurückgibt.
Als nächstes in List.map2 (<|) partialResult list3
ist es eigentlich List.map2 (<|) [foo 1 1, foo 2 2, foo 3 3, foo 4 4, foo 5 5] list3
. Für jedes Paar dieser beiden Listen rufen wir die (<|)
-Funktion auf. Für das erste Paar ist es beispielsweise (<|) (foo 1 1) 1
, was mit foo 1 1 <| 1
identisch ist foo 1 1 <| 1
, was dasselbe ist wie foo 1 1 1
, was 1
erzeugt. Für die zweite ist es (<|) (foo 2 2) 2
, was foo 2 2 2
ist, was zu 8
ausgewertet wird, und so weiter.
Diese Methode kann mit mapN
Funktionen besonders nützlich sein, um JSON-Objekte mit vielen Feldern zu decodieren, da Json.Decode
sie bis map8
.
Entfernen Sie alle Nichts-Werte aus einer Liste von Vielleichts
Angenommen, wir haben eine Liste mit Maybe
-Werten und wir möchten nur die Werte aus den Elementen extrahieren, die einen haben. Die Liste lautet beispielsweise:
list : List (Maybe Int) list = [ Just 1, Nothing, Just 3, Nothing, Nothing, Just 6, Just 7 ]
Und wir wollen [1,3,6,7] : List Int
erhalten. Die Lösung ist dieser einzeilige Ausdruck:

List.filterMap identity list
Mal sehen, warum das funktioniert.
List.filterMap
erwartet, dass das erste Argument eine Funktion ist (a -> Maybe b)
, die auf Elemente einer bereitgestellten Liste (das zweite Argument) angewendet wird, und die resultierende Liste wird gefiltert, um alle Nothing
Werte und dann die echten auszulassen Werte werden aus Maybe
s extrahiert.
In unserem Fall haben wir identity
, also ist die resultierende Liste wieder [ Just 1, Nothing, Just 3, Nothing, Nothing, Just 6, Just 7 ]
. Nach dem Filtern erhalten wir [ Just 1, Just 3, Just 6, Just 7 ]
, und nach der Wertextraktion ist es wie gewünscht [1,3,6,7]
.
Benutzerdefinierte JSON-Decodierung
Da unsere Anforderungen an die JSON-Decodierung (oder Deserialisierung) beginnen, das zu übersteigen, was im Json.Decode
-Modul verfügbar gemacht wird, haben wir möglicherweise Probleme beim Erstellen neuer exotischer Decoder. Dies liegt daran, dass diese Dekoder mitten im Dekodierungsprozess aufgerufen werden, z. B. innerhalb von Http
-Methoden, und es nicht immer klar ist, was ihre Ein- und Ausgaben sind, insbesondere wenn das bereitgestellte JSON viele Felder enthält.
Hier sind zwei Beispiele, die zeigen, wie man mit solchen Fällen umgeht.
Im ersten haben wir zwei Felder im eingehenden JSON, a
und b
, die die Seiten eines rechteckigen Bereichs bezeichnen. Aber in einem Elm-Objekt wollen wir nur seinen Bereich speichern.
import Json.Decode exposing (..) areaDecoder = map2 (*) (field "a" int) (field "b" int) result = decodeString areaDecoder """{ "a":7,"b":4 }""" -- Ok 28 : Result.Result String Int
Die Felder werden einzeln mit dem field int
Decoder decodiert, und dann werden beide Werte an die bereitgestellte Funktion in map2
. Da die Multiplikation ( *
) auch eine Funktion ist und zwei Parameter benötigt, können wir sie einfach so verwenden. Der resultierende areaDecoder
ist ein Decoder, der bei Anwendung das Ergebnis der Funktion zurückgibt, in diesem Fall a*b
.
Im zweiten Beispiel erhalten wir ein chaotisches Statusfeld, das null oder eine beliebige Zeichenfolge einschließlich leer sein kann, aber wir wissen, dass die Operation nur erfolgreich war, wenn sie „OK“ ist. In diesem Fall wollen wir es als True
und für alle anderen Fälle als False
speichern. Unser Dekoder sieht so aus:
okDecoder = nullable string |> andThen (\ms -> case ms of Nothing -> succeed False Just s -> if s == "OK" then succeed True else succeed False )
Wenden wir es auf einige JSONs an:
decodeString (field "status" okDecoder) """{ "a":7, "status":"OK" }""" -- Ok True decodeString (field "status" okDecoder) """{ "a":7, "status":"NOK" }""" -- Ok False decodeString (field "status" okDecoder) """{ "a":7, "status":null }""" -- Ok False
Der Schlüssel liegt hier in der Funktion, die an andThen
geliefert wird, die das Ergebnis eines vorherigen Nullable-String-Decoders (der ein Maybe String
ist), in das umwandelt, was wir brauchen, und das Ergebnis mit Hilfe von succeed
als Decoder zurückgibt.
Schlüssel zum Mitnehmen
Wie aus diesen Beispielen ersichtlich ist, ist die funktionale Programmierung für Java- und JavaScript-Entwickler möglicherweise nicht sehr intuitiv. Es dauert einige Zeit, sich daran zu gewöhnen, mit viel Versuch und Irrtum. Um es besser zu verstehen, können Sie elm-repl
verwenden, um die Rückgabetypen Ihrer Ausdrücke zu üben und zu überprüfen.
Das Beispielprojekt, auf das weiter oben in diesem Artikel verwiesen wurde, enthält mehrere weitere Beispiele für benutzerdefinierte Decoder und Encoder, die Ihnen möglicherweise auch beim Verständnis helfen.
Aber trotzdem, warum Ulme wählen?
Da sie sich so sehr von anderen Client-Frameworks unterscheidet, ist die Elm-Sprache sicherlich nicht „noch eine weitere JavaScript-Bibliothek“. Als solches hat es viele Eigenschaften, die im Vergleich zu ihnen als positiv oder negativ angesehen werden können.
Beginnen wir zuerst mit der positiven Seite.
Client-Programmierung ohne HTML und JavaScript
Endlich haben Sie eine Sprache, in der Sie alles tun können. Keine Trennung mehr und umständliche Kombinationen ihrer Mischung. Kein Generieren von HTML in JavaScript und keine benutzerdefinierten Vorlagensprachen mit einigen abgespeckten Logikregeln.
Mit Elm haben Sie nur eine Syntax und eine Sprache in ihrer vollen Pracht.
Gleichmäßigkeit
Da fast alle Konzepte auf Funktionen und wenigen Strukturen basieren, ist die Syntax sehr prägnant. Sie müssen sich keine Gedanken darüber machen, ob eine Methode auf Instanz- oder Klassenebene definiert ist oder ob es sich nur um eine Funktion handelt. Das sind alles Funktionen, die auf Modulebene definiert sind. Und es gibt nicht hundert verschiedene Möglichkeiten, Listen zu iterieren.
In den meisten Sprachen gibt es immer diesen Streit darüber, ob der Code in der Sprache geschrieben ist. Viele Redewendungen müssen beherrscht werden.
Wenn es in Elm kompiliert wird, ist es wahrscheinlich der „Elm“-Weg. Wenn nicht, nun, ist es sicherlich nicht.
Ausdruckskraft
Obwohl prägnant, ist die Elm-Syntax sehr ausdrucksstark.
Dies wird hauptsächlich durch die Verwendung von Union-Typen, formalen Typdeklarationen und dem funktionalen Stil erreicht. All dies inspiriert die Verwendung kleinerer Funktionen. Am Ende erhalten Sie Code, der ziemlich selbstdokumentierend ist.
Keine Null
Wenn Sie Java oder JavaScript sehr lange verwenden, wird null
für Sie zu etwas ganz Natürlichem – zu einem unvermeidlichen Bestandteil der Programmierung. Und obwohl wir ständig NullPointerException
s und verschiedene TypeError
s sehen, glauben wir immer noch nicht, dass das eigentliche Problem die Existenz von null
ist. Es ist einfach so natürlich .
Das wird nach einiger Zeit bei Elm schnell klar. Nicht null
zu haben, entlastet uns nicht nur davor, immer wieder Laufzeit-Null-Referenzfehler zu sehen, es hilft uns auch, besseren Code zu schreiben, indem alle Situationen, in denen wir möglicherweise nicht den tatsächlichen Wert haben, klar definiert und behandelt werden, wodurch auch technische Schulden reduziert werden, indem null
nicht verschoben wird Handhabung, bis etwas kaputt geht.
Das Vertrauen, dass es funktionieren wird
Das Erstellen syntaktisch korrekter JavaScript-Programme ist sehr schnell erledigt. Aber wird es wirklich funktionieren? Mal sehen, nachdem wir die Seite neu geladen und gründlich getestet haben.
Bei Elm ist es umgekehrt. Bei der statischen Typprüfung und erzwungenen null
dauert die Kompilierung einige Zeit, insbesondere wenn ein Anfänger ein Programm schreibt. Aber sobald es kompiliert ist, besteht eine gute Chance, dass es richtig funktioniert.
Schnell
Dies kann ein wichtiger Faktor bei der Auswahl eines Client-Frameworks sein. Die Responsiveness einer umfangreichen Web-App ist oft entscheidend für die User Experience und damit den Erfolg des gesamten Produkts. Und wie Tests zeigen, ist Elm sehr schnell.
Vorteile von Elm gegenüber traditionellen Frameworks
Die meisten traditionellen Web-Frameworks bieten leistungsstarke Tools für die Erstellung von Web-Apps. Aber diese Macht hat ihren Preis: eine überkomplizierte Architektur mit vielen verschiedenen Konzepten und Regeln, wie und wann sie verwendet werden. Es braucht viel Zeit, um das alles zu meistern. Es gibt Controller, Komponenten und Direktiven. Dann gibt es die Kompilierungs- und Konfigurationsphasen und die Ausführungsphase. Und dann gibt es Dienste, Fabriken und all die benutzerdefinierten Vorlagensprachen, die in bereitgestellten Anweisungen verwendet werden – all diese Situationen, in denen wir $scope.$apply()
direkt aufrufen müssen, um die Seite zu aktualisieren, und mehr.
Die Kompilierung von Elm nach JavaScript ist sicherlich auch sehr kompliziert, aber der Entwickler wird davor bewahrt, alle Grundlagen kennen zu müssen. Schreiben Sie einfach etwas Elm und lassen Sie den Compiler seine Arbeit machen.
Und warum nicht Ulme wählen?
Genug Elm zu loben. Sehen wir uns nun einige seiner nicht so großartigen Seiten an.
Dokumentation
Das ist wirklich ein großes Problem. Der Elm-Sprache fehlt ein ausführliches Handbuch.
Die offiziellen Tutorials überfliegen die Sprache nur und lassen viele Fragen unbeantwortet.
Die offizielle API-Referenz ist noch schlimmer. Vielen Funktionen fehlen jegliche Erklärungen oder Beispiele. Und dann gibt es noch solche mit dem Satz: „Wenn das verwirrend ist, arbeiten Sie das Elm Architecture Tutorial durch. Es hilft wirklich!“ Nicht die beste Zeile, die Sie in den offiziellen API-Dokumenten sehen möchten.
Das wird sich hoffentlich bald ändern.
Ich glaube nicht, dass Elm mit einer so schlechten Dokumentation weit verbreitet werden kann, insbesondere bei Leuten, die von Java oder JavaScript kommen, wo solche Konzepte und Funktionen überhaupt nicht intuitiv sind. Um sie zu verstehen, ist eine viel bessere Dokumentation mit vielen Beispielen erforderlich.
Format und Leerzeichen
Geschweifte Klammern oder Klammern loszuwerden und Leerzeichen für Einrückungen zu verwenden, könnte gut aussehen. Zum Beispiel sieht Python-Code sehr ordentlich aus. Aber für die Schöpfer des elm-format
war es nicht genug.
Mit all den doppelten Zeilenabständen und Ausdrücken und Zuweisungen, die in mehrere Zeilen aufgeteilt sind, sieht Elm-Code eher vertikal als horizontal aus. Was im guten alten C ein Einzeiler wäre, kann sich in der Elm-Sprache leicht über mehr als einen Bildschirm erstrecken.
Das mag gut klingen, wenn Sie nach geschriebenen Codezeilen bezahlt werden. Aber wenn Sie etwas mit einem Ausdruck ausrichten möchten, der 150 Zeilen früher begonnen hat, viel Glück beim Finden des richtigen Einzugs.
Umgang mit Aufzeichnungen
Es ist schwierig, mit ihnen zu arbeiten. Die Syntax zum Ändern des Felds eines Datensatzes ist hässlich. Es gibt keine einfache Möglichkeit, verschachtelte Felder zu ändern oder willkürlich Felder nach Namen zu referenzieren. Und wenn Sie die Accessor-Funktionen auf generische Weise verwenden, gibt es eine Menge Probleme mit der richtigen Eingabe.
In JavaScript ist ein Datensatz oder Objekt die zentrale Struktur, die auf viele Arten erstellt, aufgerufen und geändert werden kann. Sogar JSON ist nur eine serialisierte Version eines Datensatzes. Entwickler sind es gewohnt, mit Datensätzen in der Webprogrammierung zu arbeiten, daher können sich Schwierigkeiten in der Handhabung in Elm bemerkbar machen, wenn sie als primäre Datenstruktur verwendet werden.
Mehr Tippen
Für Elm muss mehr Code geschrieben werden als für JavaScript.
Es gibt keine implizite Typkonvertierung für Zeichenfolgen- und Zahlenoperationen, daher sind viele int-float-Konvertierungen und insbesondere toString
-Aufrufe erforderlich, die dann Klammern oder Funktionsanwendungssymbole erfordern, um die richtige Anzahl von Argumenten abzugleichen. Außerdem erfordert die Html.text
Funktion eine Zeichenfolge als Argument. Es werden viele Case-Ausdrücke benötigt, für all diese Maybe
, Results
, Typen usw.
Der Hauptgrund dafür ist das strenge Typensystem, und das kann ein fairer Preis sein.
JSON-Decoder und -Encoder
Ein Bereich, in dem mehr Typisierung wirklich hervorsticht, ist die Handhabung von JSON. Was in JavaScript einfach ein JSON.parse()
Aufruf ist, kann in der Elm-Sprache Hunderte von Zeilen umfassen.
Natürlich ist eine Art Zuordnung zwischen JSON- und Elm-Strukturen erforderlich. Aber die Notwendigkeit, sowohl Decoder als auch Encoder für dasselbe JSON-Stück zu schreiben, ist ein ernstes Problem. Wenn Ihre REST-APIs Objekte mit Hunderten von Feldern übertragen, ist dies eine Menge Arbeit.
Einpacken
Wir haben über Elm gesehen, es ist an der Zeit, die bekannten Fragen zu beantworten, die wahrscheinlich so alt sind wie Programmiersprachen selbst: Ist es besser als die Konkurrenz? Sollen wir es in unserem Projekt verwenden?
Die Antwort auf die erste Frage mag subjektiv sein, da nicht jedes Werkzeug ein Hammer und nicht alles ein Nagel ist. Elm kann glänzen und in vielen Fällen eine bessere Wahl gegenüber anderen Webclient-Frameworks sein, während es in anderen Fällen zu kurz kommt. Aber es bietet einen wirklich einzigartigen Wert, der die Web-Front-End-Entwicklung viel sicherer und einfacher machen kann als die Alternativen.
Für die zweite Frage, um nicht auch mit dem uralten „es kommt darauf an“ zu antworten, lautet die einfache Antwort: Ja. Trotz aller genannten Nachteile ist allein die Zuversicht, die Elm Ihnen gibt, dass Ihr Programm korrekt ist, Grund genug, es zu verwenden.
Das Codieren in Elm macht auch Spaß. Es ist eine völlig neue Perspektive für jeden, der an die „konventionellen“ Paradigmen der Webprogrammierung gewöhnt ist.
Im realen Einsatz müssen Sie nicht sofort Ihre gesamte App auf Elm umstellen oder komplett neu darin starten. Sie können seine Interoperabilität mit JavaScript nutzen, um es auszuprobieren, beginnend mit einem Teil der Benutzeroberfläche oder einigen Funktionen, die in der Elm-Sprache geschrieben sind. Sie werden schnell herausfinden, ob es Ihren Bedürfnissen entspricht, und es dann erweitern oder es lassen. Und wer weiß, vielleicht verlieben Sie sich auch in die Welt der funktionalen Webprogrammierung.