Ein ungleiches Trio auf einer Wandertour zum Mount Anagramm
Diese Blog-Serie bietet eine Einführung in die funktionale Programmierung mit Elm. Durch die iterative Entwicklung eines Anagramm-Generators (für die deutsche SCRABBLE-Community) soll dem Leser die funktionale Programmierung im Allgemeinen und die vorzügliche Sprache Elm im Speziellen, näher gebracht werden.
Um einen Vergleich mit herkömmlichen Frameworks im imperativen, der Objektorientierung angelehnten Stil zu ermöglichen, wird die Applikation auch in AngularJS und ReactJS geschrieben werden und Elm gegenüber gestellt.
Hier geht es direkt zu [Teil 2, 3, 4, 5, 6]
Eine Reise von Javascript nach Elm
Elm ist nicht nur eine wunderschöne Wanderregion der Schweiz mit einer vom UNESCO-Welterbe anerkannten Gebirgslandschaft, es ist auch eine unlängst erschienene funktionale reaktive Programmiersprache, welche mit Haskell geschrieben wurde und sich im Syntax an Haskell und F# anlehnt.
Elm ist für die Erstellung von Javascript-Web UI’s hervorragend geeignet, aus dem bestechenden Grund, dass es in erster Linie dafür konzipiert worden ist.
Während React.Js sich im jüngsten Technologie-Radar von 2016 bereits im Status “Adopt” befindet, hat sich Elm hochgearbeitet und ist wie Angular.Js im Status “Assess”. Es ist mit kleiner Community, und ohne viel Aufsehen zu erregen, ein ernstzunehmendes Werkzeug geworden.
Die Programmierung mit Javascript hat eine lange und spannende Entwicklung durchgemacht. Erst belächelt, dann missbraucht, lange nicht ernst genommen, endlich zu neuem Ruhm gelangt mit node.js und nun in den höchsten Tönen gefeiert. Aber zu den höchsten Tönen gehört auch Geschrei und Gekreische!
Eine umfangreiche und unterhaltsame Beschreibung vom Aufstieg und Fall der Sprache Javascript hat Flaki geschrieben.
Diese Blog-Serie ist sowohl für Anfänger der Frontend-Entwicklung, als auch für erfahrene Javascript-Programmierer geschrieben. Erstere sollten vielleicht die Javascript-Abschnitte überspringen und direkt nach Elm aufbrechen, die anderen finden hier hoffentlich ein Stück Glück bei ihrer Suche nach alternativen (Wander)-Wegen und wirksamen Werkzeugen.
Klären wir aber zuerst einmal die Begriffe, bevor wir untersuchen, ob Elm als richtungsweisend bezeichnet werden kann und warum funktionale Programmierung von Entwicklern genutzt und (anderen) Entscheidungsträgern gefordert, gefördert und getragen werden sollte.
Begriffe
Von der Github-Seite des Autoren Evan Czaplicki vernimmt man:
„Elm is a type inferred, functional reactive language that compiles to HTML, CSS, and JavaScript.“
- ELM ist KEIN gültiges SCRABBLE-Wort. DOM schon.
- FRP oder funktionales reaktives Programmieren stellt eine Methode dar, um auf deklarative Art reaktive Systeme zu erstellen.
- Reaktive Programmierung findet überall dort eine Anwendung, wo man auf Systeme trifft, die von der Verarbeitung von vielen externen Eingaben abhängen (das macht sie reaktiv), also in Domänen wie IOT, graphischen Simulationen (sprich: Games) oder Benutzeroberflächen, um nur wenige zu nennen.
- Funktionale Programmierung im ML-Stil existiert nun schon seit über 45 Jahren und predigt die Einhaltung eines puren Programmierstils, welcher mit unveränderbaren Werten operiert (Stichwort: immutability), um ungewünschte Seiteneffekte zu reduzieren oder ganz zu verhindern. Die meisten Funktionen in Elm sind deshalb auch “pure”: wenn man eine Funktion mit den gleichen Argumenten mehrmals und zu unterschiedlichen Zeitpunkten aufruft, erhält man immer das gleiche Resultat. Dies ist NICHT der Fall für Funktionen in anderen Sprachen, welche sich potentiell auf global verändernden Zustand beziehen. Diese Eigenschaft wird sich noch als äusserst vorteilhaft erweisen (z.B. für die Parallelisierung) und soll in dieser Serie genauer untersucht werden.
- Von Typinferenz wird gesprochen, wenn der Compiler einen Typ ableiten, resp. schlussfolgern kann, ohne dass dieser deklariert werden muss. Dies lässt statische Typisierung zu, Deklarationen und Signaturen können auf ein Minimum beschränkt werden.
- Deklarativ schlussendlich bedeutet, dass wir beim Programmieren nicht beschreiben WIE etwas getan werden soll, sondern WAS es zu tun gibt.
Yadayada! Ist diese Blog-Serie für mich? (YOLO!)
In dieser Serie geht es darum, eine fundierte Aussage erreichen zu können über die Vor- und Nachteile der Entwicklung mit Elm im Vergleich zu anderen bekannten Web-Frameworks wie React.JS und Angular.
Wer sich nur für das Resultat interessiert, muss sich noch etwas gedulden. Das Ganze ist, der Zeit entsprechend, ein agiler Prozess in der Entstehung.
Wer aber schon einmal Web-GUIs entwickelt hat oder es gerne tun möchte, wer je Erfahrung gemacht hat mit einem Template-System (ColdFusion, JSP, ASP.NET, PHP, Jade, Handlebars, Mustache, Razor, waren es allein für mich in dieser endlosen Liste) oder mit einem Binding-Framework (jQuery, Knockout.JS, Backbone.JS, AngularJS und React.JS), oder wer sich wegen der grossen Auswahl und endloser Fülle an Tutorials nicht entscheiden kann (auch eine Zeiterscheinung), der sollte sich die Programmiersprache Elm anschauen, bzw. diese Blog-Serie lesen und bookmarken.
Funktionale Programmierung mit Elm ist eine andere Art der Programmierung: direkter, problembezogener, korrekter. Sie zwingt den Programmierer, erst das Problem zu analysieren und zu formalisieren, bevor er eine elegante Lösung aus ihm herauskitzelt.
Zudem muss er sich schon von Anfang an auch um den Fehlerfall kümmern, da keine der Funktionen im Elm-Empirium sich diese Blösse geben würden, einen undefinierten Zustand zu hinterlassen, nicht einmal die “unpuren”.
Und Elm unterstützt sowohl den Einsteiger, als auch den erfahrenen Web-Entwickler, wo es nur kann:
- Der Elm-Compiler liefert sinnreiche, detaillierte Erklärungen, Vorschläge und Kommentare. Dies war sogar Schwerpunkt der letzten zwei Releases.
- Der mitgepackte Elm-Reactor ist ein Entwicklungs-Webserver, welcher Elm-Module automatisch in ein Javascript-Bundle kompiliert, dieses in eine HTML-Seite einbettet und aushändigt.
- Der “zeitreisende” Debugger setzt dem Ganzen dann noch die Haube auf: er erlaubt es, den Zustand der Applikation zurück- und wieder vorzuspulen. Das kennt man als “Live-Coding”, was als solches bekannt gemacht wurde durch den Visionär Bret Victor
Wie einfach sich der Einstieg mit Elm gestaltet, soll nun demonstriert werden.
Ich kann nicht warten
Das kenn ich. Los gehts!
Elm Plattform installieren
Mit npm install -g elm oder Installationsprogramm
Installation überprüfen
Die folgenden Tools werden installiert:
– elm-compiler – der ominöse Übersetzer
– elm-repl – (benötigt node.js): die Read-Eval-Print-Loop-Konsole für Elm
– elm-make – das Build-Tool für Elm
– elm-reactor – der Entwicklungs-Web-Server mit zeitreisendem Debugger
– elm-package – der fleissige Package-Manager von Elm
Die aktuell installierte Version findet man z.B. mit elm-reactor -v . (sollte sich nach der Installation im Pfad befinden).
Projekt aufsetzen
Projekt-Verzeichnis anlegen und elm und git initialisieren:
mkdir elm-anagrams && cd elm-anagrams elm-package install -y git init (echo elm-stuff/ elm.js) > .gitignore git add * git commit -m "initial commit"
Die einzig relevante Zeile ist natürlich elm-package install -y , aber die anderen gehören zum guten Ton und zeigen auch gleich, welche generierten Artifakte von der Versionierung ausgeschlossen werden sollten.
Editor-Support
Als Editor für Elm kann ich Sublime Text 3 empfehlen, welches ein Package Elm Language Support anbietet mit Intellisense, Syntax-Highlighting und Build-Integration mit Fehler-Highlighting.
Hello.elm
Die Datei wird im Root-Verzeichnis angelegt, mit folgendem Inhalt:
import Graphics.Element exposing (..) main = show "Hello, Elm!"
Anschliessend wird der Reactor gezündet von der Kommandozeile aus mit
elm-reactor
Jetzt kann im Browser auf http://localhost:8000 navigiert, Hello.elm
angeklickt und gestaunt werden.
Was ist hier genau geschehen?
Der Befehl elm-package install -y hat die Datei elm-package.json angelegt mit dem folgenden Inhalt:
{ "version": "1.0.0", "summary": "helpful summary of your project, less than 80 characters", "repository": "https://github.com/user/project.git", "license": "BSD3", "source-directories": [ "." ], "exposed-modules": [], "dependencies": { "elm-lang/core": "3.0.0 <= v < 4.0.0" }, "elm-version": "0.16.0 <= v < 0.17.0" }
Mal abgesehen von der genauen Bedeutung der unbenutzten Eigenschaft exposed-modules ist der Inhalt selbsterklärend. Bei den Abhängigkeiten (dependencies ) gibt es genau eine davon, nämlich diejenige auf das Kern-Paket des Elm-Frameworks.
Beim Klicken der Hello.elm -Datei im Browser läuft im Reactor folgendes ab:
- Die deklarierten Abhängigkeiten werden heruntergeladen und mit elm-make sowohl nach Javascript transpiliert, als auch in ein eigenes Binärformat kompiliert. Die Resultate werden im Verzeichnis elm-stuff abgelegt (Daher der Eintrag im .gitignore ).
- Auch die Zieldatei (Hello.elm ) wird entsprechend kompiliert und aus den abhängigen Erzeugnissen wird das Bundle (unsere Applikation) elm.js generiert (wird ebenfalls ignoriert im git).
(elm.js - generiert)
...
Elm.Main.make = function (_elm) {
...
var _op = {};
var main = $Graphics$Element.show("Hello World");
return _elm.Main.values = {_op: _op,main: main};
};
- Es wird automatisch eine In-Memory Html-Seite erzeugt, in welche der elm.js -Skript eingebettet wird, und diese Seite wird dann via HTTP ausgeliefert (Seite im Browser mit Developer-Konsole (F12) inspizieren):
<script>var runningElmModule = Elm.fullscreen(Elm.Main);</script>
- Unser Elm-Programm
main
wird ausgeführt wenn die Seite fertig geladen ist.
main = show "Hello World"
Das Hauptprogramm besteht einzig aus dem Aufruf der Funktion show aus dem Elm-Core-Modul Graphics.Element . Dieses Modul enthält einen Typ namens Element , welcher ein rechteckiges, graphisches Element bekannter Grösse darstellt, das auf dem Bildschirm dargestellt werden kann.
show hat die folgende Signatur (lies: Name der Funktion : In1 -> In2 -> InN -> Out ):
show: a -> Element
Der kleingeschrieben Platzhalter a steht für einen beliebigen Typen, d.h. der Typ des einzigen Eingabe-Parameters wird nicht spezifiziert, also kann diese Funktion jegliche Struktur akzeptieren und im Browser darstellbar machen.
In Elm ist das in etwa so implementiert (vorerst ohne Erläuterung für die Neugierigen):
show : a -> Element show value = leftAligned (Text.monospace (Text.fromString (toString value)))
Beim Aufruf von show „Hello World“ wird also der String (Elm-Datentyp) “Hello World” in ein graphisches Element verwandelt und ausgegeben. In der Entwickler-Konsole des Browsers stellt man das folgende DOM-Element fest:
<span style="font-family:monospace;">"Hello World"</span>
Abmarsch
So, damit haben wir das Nötigste eingepackt für eine spannende Wanderung im Elm-Gebirge. Bevor wir aber los marschieren, schauen wir doch unseren Kollegen Angular und React beim Packen zu.
Was alles getan werden muss, um mit Angular2/TypeScript oder ReactJS/Typescript zum gleichen Basis-Resultat zu gelangen, wird Inhalt des nächsten Blog-Eintrags sein.
Berg heil? bis zum nächsten Teil…