
kontaktiere uns


Dies ist der zweite Teil meines Artikels über die Programmiersprache Elm. Zum ersten Artikel Ich habe mich auf die Hauptfunktionen konzentriert, die Elm zu einer interessanten Alternative zu JavaScript machen.
Hier konzentriere ich mich auf die historische Perspektive, die Elm einen Blick wert macht, und werde mich eingehender mit den beiden Hauptmerkmalen der Sprache befassen: funktionale Reinheit und statische Typisierung. Danach werde ich einige gute Beispiele dafür nennen, wer Elm in der Produktion verwendet.
Der beste Weg, die Zukunft vorherzusagen, besteht darin, sie zu erfinden.
— Alan Kay, Designer der Programmiersprache Smalltalk
Vor 2013 folgten die meisten Frontend-Programme sehr imperativen Ansätzen. Frameworks wie Backbone oder Angular v1 haben sich vom Model-View-Controller-Muster (MVC) inspirieren lassen und die Mutation und Beobachtung der View-Modelle zum Kern ihrer Darstellungslogik gemacht. Der Umgang mit so vielen Zuständen und Ereignissen war für viele Webentwickler schwierig und unbequem. Der einfache Datenfluss ihrer Share-Nothing-Web-Frameworks im Backend ließ sie nach den alten Zeiten seufzen, als JavaScript und AJAX ihre Welt noch nicht erobert hatten.
Reagieren war das erste Projekt, das dieses zwingende Kartenhaus ins Wanken brachte. Das markanteste Merkmal dieses Projekts ist das virtuelle DOM, das es Programmierern ermöglicht, sich ihre Webseite als etwas vorzustellen, das effizient neu berechnet und ohne große Kosten neu gerendert werden kann.
Diese Effizienz ermöglicht es React, das Frontend zu modellieren: Eine Webseite besteht aus einem Baum von Komponenten, und jedes Mal, wenn der Benutzer mit ihnen interagiert, wird das gesamte Erscheinungsbild der Seite auf der Grundlage des neuen Status, der in diesen Komponenten gespeichert ist, neu berechnet. Sie müssen das DOM nicht mehr aktiv ändern; React erledigt das für Sie.
Obwohl React die Modellierung von Webanwendungen erleichterte, wurde keine Möglichkeit vorgeschrieben, die Geschäftslogik von der Rendering-Logik zu trennen. Facebook schlug eine Architektur namens Flux vor, um dieses Problem zu lösen, obwohl viele der früheren Implementierungen komplexer als nötig waren und Zeit in Anspruch nahmen, um an Bedeutung zu gewinnen.
Es war 2015, dass Redux erschien und entstand als einfachere Implementierung der Flux-Architektur. Die Hauptidee ist, dass der Status der Anwendung in einem einzigen Zustandsbaum gespeichert werden sollte und dass Änderungen an diesem Baum als Reduktionsfunktion modelliert werden sollten. Dies selbst sollte das Ergebnis der Zusammensetzung mehrerer Reduktionsfunktionen sein, von denen jede für einen Teil des Zustands der Anwendung verantwortlich ist. Sie modellieren die Logik, um jede Aktion zu verarbeiten und eine neue Version des Zustands zu erhalten.
Redux ist eine einfache Bibliothek mit einem sehr kleinen Kern (rund ein Dutzend kleiner Funktionen). Es ist leicht zu verstehen, angenehm zu bedienen und ermöglicht die Verwendung von coolen Entwicklungsfunktionen wie zeitreisenden Debuggern und dem erneuten Laden von Modulen im laufenden Betrieb.
Trotz der Einfachheit ist der Ansatz nicht intuitiv und fühlt sich JavaScript fremd an. Das Schreiben von Reduktionsfunktionen, die die vorherige Version des Zustands nicht ändern, ist nicht trivial und es wird sogar empfohlen, eine Bibliothek zu verwenden, um den Zustand auf unveränderliche Weise zu behandeln. Es ist daher keine große Überraschung, dass Redux Autor ist hat es sehr deutlich gemacht dass eine solche Architektur stark von Elm inspiriert ist.
Mut bedeutet zu wissen, wovor man sich nicht fürchten muss.
— Platon
Wenn Elms Architektur erfolgreich war, nachdem sie mit einer außerirdischen imperativen Technologie wie JavaScript kombiniert wurde, selbst auf den Preis, dass Entwickler aus ihrer Komfortzone genommen wurden, wie schön kann es dann sein, wenn sie sich von der „Hermeneutik“ der dynamischen Typisierung und der Unsicherheit willkürlicher Zustandsmutationen fernhält? Gibt es noch mehr, was man aus dieser Lode mitnehmen kann? Gibt es noch andere Geheimnisse unter Elm?
Wenn ein Softwareprojekt beginnt, ist der gesamte Code schön, rosa und glänzend. Dann sieht man sich mit Deadlines, Bugs, Hotfixes, Änderungen des Umfangs, Unternehmenszusammenschlüssen, Akquisitionen und einer Horde überlasteter Entwickler konfrontiert, die den Code ändern, während der ursprüngliche Autor im Urlaub ist, krank oder tot ist. Nach der Alterung hat jede Codezeile den Höhepunkt ihrer Geschichte hinter sich, und oft ist niemand da, um das zu sagen.
Bei schlecht gealterten Projekten lügen Variablennamen, Funktionsnamen, Klassennamen und Typnamen lügen. Wenn wir jedoch dynamisch typisierte Sprachen wie JavaScript verwenden, geben uns nur Namen und Tests Informationen über die Semantik eines Programms und darüber, wie es geändert und verbessert werden kann.
Statisch typisierte Sprachen hingegen überprüfen die Semantik des Projekts und stellen dessen Richtigkeit sicher. Die Namen mögen falsch sein, aber wir wissen, welche Operationen zu welchen Typen passen und der Compiler prüft automatisch, ob unsere Änderungen sinnvoll sind.
Die Informationen, die den Entwicklern von solchen Systemen zur Verfügung gestellt werden, sind tausend Namen wert, und diese Systeme sind einfacher zu ändern und erfordern viel weniger Tests, da das Typsystem mehrere Fehler vermeidet, auf die sonst ein von einem Menschen entwickelter automatisierter Test überprüft werden müsste.
Das Problem mit Typen ist, dass sie in den meisten Sprachen beim Entwickeln von neuem Code und beim Debuggen oder Experimentieren mit dem Verhalten des Codes im Weg stehen. Normalerweise fügen Typen der klaren Sprache, die Entwickler beim Programmieren mit der Maschine anderer Entwickler führen, eine Menge unnötigen Lärm hinzu.
Bei Typen sind Entwickler auch gezwungen, mehr zu schreiben - und obwohl das vielleicht nicht sehr schlecht ist, wenn man die automatische Vervollständigung des Editors verwendet, ist es eine Katastrophe, wenn man in interaktiven Umgebungen wie der Befehlszeile oder dem Debugger experimentiert. Die Sperre, wenn der Code voller Typinformationen ist, erzeugt viel Trägheit und bedeutet einen enormen Verlust an Agilität. Die Wahl zwischen statischer und dynamischer Typisierung ist daher ein Kompromiss zwischen Benutzerfreundlichkeit und Wartbarkeit.
Es ist daher nicht verwunderlich, dass wir mehrere Initiativen finden, die statische Typisierung in die JavaScript-Welt bringen. Die berüchtigtsten sind Fluss (Facebook), Typoskript (Microsoft) und Dart (Google). Dabei handelt es sich zwar nicht um Änderungen an der JavaScript-Sprache selbst, aber es handelt sich entweder um Supersets der Sprache, die das Tippen ermöglicht, oder um ähnliche Sprachen, die direkt mit der JavaScript-Sprache konkurrieren. Dies erhöht den Druck auf die Entwickler, zu einem statisch typisierten Ansatz überzugehen, da sich auch die Tools und Bibliotheken in diese Richtung bewegen.
Allen diesen Ansätzen ist jedoch ein gemeinsamer Trend gemein: Um die statisch typisierte Sperrigkeit zu vermeiden, haben sie sich dafür entschieden, Typen optional zu machen. Somit steht es den Entwicklern — je nach Kontext — frei, ihrem Code entweder Typinformationen hinzuzufügen oder sich dafür zu entscheiden, ohne Typprüfung zu arbeiten — und die damit verbundene Typsicherheit.
Die Ergebnisse sind besser als ohne Typen, aber immer noch relativ begrenzt. Da die Typen schrittweise sind, ist es oft unmöglich zu überprüfen, ob die Typsignaturen sinnvoll sind. Der Umfang der Überprüfung hängt stark von der Disziplin des Entwicklers ab und davon, ob die verwendeten Bibliotheken Typinformationen für dasselbe Typsystem haben.
Elms Ideen stammen aus der typisierten Funktionswelt, in dem jahrzehntelange Entwicklung zur Verwendung von Typinferenz führte. Im Gegensatz zur dynamischen oder schrittweisen Typisierung überprüft der Compiler die Typen im Code. Im Gegensatz zu den meisten statischen Typsystemen müssen die Typinformationen jedoch nicht explizit vom Programmierer eingegeben werden und werden vom Compiler abgeleitet.
Der Programmierer kann sich dafür entscheiden, seinen Funktionen später Typsignaturen hinzuzufügen, obwohl diese Typsignaturen von den Funktionsdefinitionen getrennt sind. Auf diese Weise werden sie für jeden, der den Code später liest oder bearbeitet, nicht verwirrend.
Es sollte jedoch beachtet werden, dass Elm zwar von funktionalen Sprachtypsystemen inspiriert ist, sich jedoch nicht an deren komplexeste Merkmale hält. Es übernimmt das, was Frontend-Programmierern nützlich erscheint, und verfolgt einen pragmatischen Ansatz. Es ist, als ob JavaScript für seine aktuelle Verwendung neu gestaltet wurde.
Wahre Weisheit kommt zu jedem von uns, wenn wir erkennen, wie wenig wir über [...] die Welt um uns herum verstehen.
— Sokrates
Elm ist eine rein funktionale Programmiersprache. Das bedeutet, dass all ihre Ausdrücke über eine einfache Eigenschaft verfügen, die heißt Referenzielle Transparenz woraus sich mehrere interessante Konsequenzen ergeben. Wir beschreiben weiter, was ist Referenzielle Transparenz und dann die Konsequenzen untersuchen.
Ein Ausdruck oder eine Funktion wird als referentiell transparent bezeichnet, wenn sich ihre Ergebnisse logisch verhalten. Mit anderen Worten, wenn ein referentieller transparenter Ausdruck mit einer bestimmten Eingabe versehen ist, führt er immer zu demselben Ergebnis.
Wenn wir an das mathematische Konzept von denken wirken vielleicht stellen wir fest, dass es dieses Anwesen tatsächlich genießt. Es ist eigentlich ziemlich schwer vorstellbar, wie es anders sein könnte. Die Funktion, die die Fläche eines Quadrats berechnet, ergibt bei Eingabe von 2 Metern immer 4 Quadratmeter und verhält sich auch immer so. Es gibt keine Möglichkeit, eine Funktion zu schreiben, die die Seite des Quadrats als einzelne Eingabe verwendet und dafür sorgt, dass sie zu unterschiedlichen Zeitpunkten unterschiedliche Werte zurückgibt.
Zumindest in der Mathematik, über die die meisten von uns nachgedacht haben, scheinen Mathematiker eine solche Abstraktion als ein sehr wichtiges - wenn nicht sogar das wichtigste - Instrument gewählt zu haben, um die Welt zu modellieren und über sie nachzudenken. Ich würde sagen, das war kein Zufall.
Wenn eine Funktion immer die gleichen Ergebnisse liefert, können wir sie auswerten und testen, ohne etwas außer ihren Ein- und Ausgaben zu berücksichtigen. Wir wissen genau, was wir anbieten müssen und was uns erwartet, wenn wir darüber nachdenken, es zu modellieren und zu testen.
Aber referentielle Transparenz ist in den meisten Programmiersprachen keine Realität. Mit der Entwicklung digitaler Computer entschieden sich die meisten Programmiersprachen dafür, die Welt so zu modellieren, wie die Maschinen intern funktionieren, und nicht so, wie Mathematiker die Welt modellierten.
Seit der Entwicklung der ersten Compiler gab es Versuche, dem ersten Weg zu folgen. Die Computer waren zu dieser Zeit jedoch nicht leistungsfähig genug, um mit solchen Rechenmodellen umzugehen, und die maschinenähnliche Art, die Welt zu modellieren, wurde zu einem tief verwurzelten Bestandteil unserer Informatikkultur.
Diese maschinenähnliche Art, die Welt zu modellieren (imperative Programmierung), basiert hauptsächlich auf einem mutierenden Zustand. Darin können Berechnungen modelliert werden, indem der Zustand einer Speichereinheit gelesen und geändert wird, bis ein Endzustand berechnet ist. Beim Hinzufügen von Funktionen zu diesem Modell entschieden sich die meisten Sprachen dafür, diesen Zustand auf alle Funktionen zu verteilen, wodurch die referentielle Transparenz unterbrochen wurde.
Funktionen hängen nicht von ihrer Eingabe ab; ihre Ergebnisse hängen vom Speicher ab, den sie lesen und schreiben, und sie geben oft keinen Wert zurück, sondern dienen lediglich dem Zweck, die Werte im gemeinsamen Zustand zu ändern.
Im Gegensatz dazu teilen sich unsere Funktionen bei der rein funktionalen Programmierung keinen Zustand. Und wenn kein Zustand gemeinsam genutzt wird, gibt es keinen Grund mehr, den Status der verwendeten Werte zu ändern. Das Ziel unserer Funktionen besteht nicht mehr darin, Werte zu ändern, sondern sie werden als Eingaben zur Berechnung anderer Werte verwendet. In der rein funktionalen Programmierung ist jede Definition unveränderlich.
Elm passt in diese Sprachfamilie und daher sind alle ihre Definitionen unveränderlich und alle ihre Funktionen sind referentiell transparent.
Reine Funktionen allein sind nutzlos. Wir führen Berechnungen durch, weil wir Eingabewerte aus der Außenwelt lesen wollen und weil wir sie entsprechend den Werten unserer Berechnungen ändern wollen. Die Außenwelt ist also wie ein Zustand, der durch unser Programm im gleichen Sinne verändert wird wie die Erinnerung an unsere imperativen Programme.
Einige funktionale Programmiersprachen fanden mathematische Wege, um dieses Problem elegant in rein funktionalem Stil und auf allgemeine Weise zu lösen, allerdings unter Verwendung einiger Konzepte, die sich als ziemlich schwer zu verstehen erwiesen haben.
Elm hat einen anderen Ansatz. Seine Laufzeit und Architektur verbergen dieses Problem vor uns. Unser Programm besteht letztlich aus einer Reihe von Funktionen, mit denen berechnet wird, was dem Benutzer bei einer bestimmten Abfolge von Benutzeraktionen oder anderen Umgebungseingaben angezeigt werden soll.
Im Grunde kümmern sich die Elm-Laufzeit und -Architektur um die Interaktion mit der Welt in diesem speziellen Bereich, was sie auf angenehme und einfache Weise macht.
Die Zukunft ist nicht auf einer Schiene angelegt. Es ist etwas, das wir entscheiden können, und soweit wir keine bekannten Gesetze des Universums verletzen, können wir wahrscheinlich dafür sorgen, dass es so funktioniert, wie wir es wollen.
— Allan Kay
Das Ökosystem von Elm ist noch ziemlich unreif und entwickelt sich langsam. Die Qualität der Bibliothek ist jedoch oft sehr gut und der Compiler gibt uns zahlreiche Garantien für ihre Stabilität. Derzeit ist Elm für kleine dynamische Webseitenkomponenten oder für einfache Webseiten geeignet, die kein serverseitiges Rendern erfordern.
Einige wichtige Funktionen werden voraussichtlich nur mit der folgenden Version (0.19) verfügbar sein:
Das Hauptziel dieser Version wird darin bestehen, ein akzeptables System für die Entwicklung komplexer einseitiger Webanwendungen bereitzustellen.
Sich zu verkleiden ist unweigerlich ein Ersatz für gute Ideen. Es ist kein Zufall, dass technisch unfähige Geschäftstypen als „Anzüge“ bezeichnet werden.
— Paul Graham
Trotz ihrer Unreife gibt es in der Branche bereits einige Erfolgsgeschichten:
Kontext ist 80 IQ-Punkte wert.
— Alan Kay
Elm ist stark von Haskell inspiriert und inspiriert Purescript stark. Hier ein Überblick darüber, worum es dabei geht:
Haskell ist bei weitem die Sprache, die Elm am meisten beeinflusst hat. Es ist der aktuelle Standard für Programmiersprachen mit fauler Typisierung und wurde von einem Ausschuss von Wissenschaftlern aus diesem Bereich entwickelt. Die Hauptunterschiede zu Elm sind:
Reines Drehbuch liegt irgendwo zwischen Haskell und Elm. Beide entwickeln sich gemeinsam weiter und beeinflussen sich gegenseitig, während sie wachsen. Es zeichnet sich durch folgende Hauptmerkmale aus:
Aufgrund seines komplexen Typsystems halte ich es für die meisten JavaScript-Programmierer nicht für machbar, in Purescript einzusteigen. Aufgrund seiner einfachen (und unsicheren) Interaktion mit JavaScript ist es jedoch eine interessante Option für Leute, die möglicherweise die Sprungbretter von Elm oder Haskell durchgemacht haben.
Es gibt drei Projekte im Ökosystem dieser Sprache, die versuchen, dasselbe Problem wie die Elm-Architektur anzugehen:
Elm ist eine interessante Frontend-spezifische Programmierumgebung, die ein unausgereiftes Software-Ökosystem unterstützt. Aufgrund des Sprachdesigns bietet dieses junge Ökosystem letztendlich untypisch starke Sicherheitsgarantien. Viele davon stammen aus dem benutzerfreundlichen Compiler, der garantiert, dass keine Laufzeitfehler auftreten.
Es ist sehr angenehm, damit zu arbeiten und ermöglicht eine einfache Entwicklung und Umgestaltung mit beispielloser Sicherheit und Freude. Es wurde entwickelt, um die Bedürfnisse eines modernen JavaScript-Programmierers zu erfüllen und um den Einstieg einfach zu machen.
Derzeit ist es noch nicht bereit, komplexe einseitige Webanwendungen zu entwickeln, da das serverseitige Rendering und einige wichtige Optimierungen fehlen. Die nächste Version (0.19) wird diese Probleme voraussichtlich beheben, und obwohl sie noch nicht fertig ist, ist es möglicherweise keine gute Idee, sie für mehr als kleine Anwendungen oder Komponenten zu verwenden.
Es lohnt sich, diesem Projekt, das das Potenzial zu haben scheint, ein sehr wettbewerbsfähiges Instrument zu werden, etwas Aufmerksamkeit zu schenken.
Bei Imaginary Cloud wir haben ein Expertenteam für Softwareentwicklung. Wenn Sie der Meinung sind, dass Sie Hilfe bei Ihrem digitalen Projekt gebrauchen könnten, schreiben Sie uns hier!
Fanden Sie diesen Artikel hilfreich? Diese könnten dir auch gefallen!
Rails-Entwickler mit mehr als 10 Jahren Erfahrung mit verschiedenen Technologien. Ich interessiere mich für funktionale Programmierung.
People who read this post, also found these interesting: