Elixir – mit Erlangs Erben in die Zukunft

Sprachen  –  10 Kommentare

Trotz seines zarten Alters von gerade einmal drei Jahren ist Elixir aufgrund seiner Wurzeln bereits einen Blick wert. Vor allem, da die Sprache eine Reihe von Eigenschaften mitbringt, die sich für die vernetzte Zukunft als vorteilhaft erweisen könnten.

Eine aktuelle McKinsey-Studie bescheinigt dem Internet der Dinge eine rosige Zukunft. Die Verfasser der Studie gehen von einem ökonomischen Gewinn in Höhe von elf Billionen Euro aus. Beispielsweise können Städte und Kommunen dadurch profitieren, dass man Mülltonnen nur noch dann leert, wenn sie tatsächlich voll sind. Aber auch der Straßenverkehr soll sich mit automatischer Stauumfahrung effizienter steuern lassen.

Die Ideen für diese schöne neue Welt sind nahezu unbegrenzt – allerdings braucht es zu ihrer Umsetzung besonders effektive Programmiersprachen. Eine von ihnen schickt sich gerade an, die Entwicklung des Internets der Dinge massiv voranzutreiben: Elixir, eine vergleichsweise neue Sprache, die auf Erlang setzt.

Bis vor wenigen Jahren funktionierte das Internet denkbar einfach. Üblicherweise sendeten Geräte Signale an einen Server, der bei Bedarf eine Antwort zurückgab. Die Kommunikation lief dabei zumeist über HTTP. Für die Anforderungen, die das Internet der Dinge stellt, reicht das allerdings nicht mehr. Denn jetzt sollen ganz unterschiedliche Geräte eigenständig miteinander kommunizieren können. So sind moderne Häuser häufig bereits mit zahlreichen Sensoren für Licht, Temperatur, Küchengeräte, TV und Zugangskontrollen ausgestattet, die es gilt miteinander zu vernetzen.

Mit IPv6 lassen sich die Geräte zukünftig zudem jeweils mit einer eigenen IP-Adresse ausstatten. Wie viele es letztendlich sein werden, lässt sich aktuell nur schätzen. Gartner geht von bis zu 26 Milliarden, IDC sogar von 212 Milliarden IP-fähigen Geräten weltweit aus.

Erlang als Ursprung

Kommt die Rede auf Erlang, ist zumeist nicht nur die Sprache an sich gemeint. Vielmehr umfasst die Bezeichnung fast immer auch die Bibliothek und die Laufzeitumgebung. Genau genommen handelt es sich bei diesem Paket allerdings um Erlang/OTP, wobei OTP für The Open Telecom Platform steht. Der Einfachheit halber kommt meistens, so auch in diesem Artikel, schlicht der Begriff Erlang zum Einsatz.

Erlang gibt es mittlerweile seit mehr als 25 Jahren. Das verwundert, schließlich ist die Sprache immer noch in weiten Kreisen unbekannt. Das liegt aber eher an der traditionell schwachen Position funktionaler Sprachen als an bestimmten Schwächen von Erlang. Trotz der vergleichsweise geringen Bekanntheit findet sich Erlang in vielen Anwendungen wie Facebook, GitHub, bei T-Mobile und WhatsApp.

Entwickelt wurde die Programmiersprache ursprünglich für den Einsatz in der Telekommunikation. Die Anforderungen, die in dieser Branche an eine Sprache gestellt werden, lassen sich fast unverändert auf das Internet der Dinge übertragen. Entscheidend sind nämlich dort wie hier hohe Verfügbarkeit und Parallelität. Da sich Erlang-Code zur Laufzeit verändern lässt, müssen damit verfasste Applikationen bei Anpassungen nicht angehalten werden. Dadurch sind die Anwendungen vergleichsweise gut erreichbar: Offiziellen Angaben zufolge geht man bei Erlang-Systemen von einer Verfügbarkeit im Bereich von 99 Prozent pro Jahr aus. Dabei ist allerdings zu beachten, dass dies zwar beim ersten hören gut klingen mag, drei bis vier Tage Ausfallzeit pro Jahr allerdings je nach Anwendung sehr viel sein können.

Ein Kernelement von Erlang sind die sogenannten Prozesse, die sich am ehesten mit Threads vergleichen lassen, allerdings weniger umfangreich sind. Die Sprache ermöglicht den Start mehrerer tausend Prozesse, die sich dann auf CPUs verteilen lassen.

Hallo, Erlang!

Wie in der Entwicklergemeinde üblich, soll auch im vorliegenden Artikel ein Hallo-Welt-Beispiel dazu dienen, einen ersten Eindruck von Erlang zu bekommen. Aber Achtung: Die Syntax ist für dieses sehr einfache Beispiel, beziehungsweise das daraus resultierende Ergebnis, vergleichsweise aufwendig und umständlich. Das liegt allerdings nicht an der Komplexität Erlangs, sondern eben daran, dass die Sprache auf komplexe Anwendungen spezialisiert ist.

Hier der angenommene Inhalt einer welt.erl:

-module(hallo).  % Das ist der Modulname.
-export([hallo_welt/0]). % Die Funktion wird deklariert.

hallo_welt() -> io:format("Hallo, Welt! ~n").
% Legt fest, was nach dem Aufruf der Funktion geschehen soll.

Der Code beschreibt ein sehr einfaches Erlang-Modul. Er gibt lediglich den definierten Begrüßungstext aus. Anschließend lässt sich innerhalb der Erlang-Shell, die über ~$ erl aufgerufen wird, das Modul kompilieren und die Funktion ausführen.

Eshell V5.5.5  (abort with ^G)
1> c(hallo).
{ok,hallo}
2> hallo:hallo_welt().
Hallo, Welt!
ok
3>

Mit c(hallo). starten Nutzer den Kompiliervorgang, hallo:hallo_welt(). ruft aus dem kompilierten Modul hallo die Funktion hallo_welt() auf.

Das Beispiel zeigt natürlich nur eine einfache Anwendung, die aber trotzdem vermitteln sollte, wie sich Erlang grundlegend verhält. Ausführliche Informationen zur Sprache und den aktuellen Entwicklungen gibt es auf der Projektseite. Dort finden sich übrigens nicht nur eine ausführliche Dokumentation, sondern auch zahlreiche praxistaugliche Beispiele.

Elixir

Und dann kam Elixir

Funktionale Sprachen gibt es zahlreich und seit langer Zeit; genannt seien zur Einordnung JavaScript, Python, Ruby und XSLT. Alle haben durchaus Vor- und Nachteile. Erlang gehört ebenfalls zur Gruppe der funktionalen Sprachen und erinnert von der Syntax her an das bei vielen Entwicklern ungeliebte Prolog. An dem Punkt setzt Elixir an: Die Sprache nutzt die Vorteile von Erlang, kommt allerdings mit einer deutlich besseren und eleganteren Syntax daher.

Die Elixir-Entwicklung wurde im Jahr 2013 von José Valim, der übrigens auch Mitglied des Ruby on Rails Core Teams ist, initiiert. Hauptgrund für den Wunsch nach einer neuen Sprache war die Erweiterbarkeit der Erlang VM.

Zunächst ein kurzer Blick auf die Besonderheiten von Elixir. Wichtig sind vor allem die Möglichkeiten zur Rekursion und die sogenannten Funktionen höherer Ordnung. Wie im folgenden Codeausschnitt zu sehen ist, akzeptieren sie andere Funktionen als Argumente:

defmodule Content do
def foursome(value, function) do
4 * function.(value)
end
end

Elixir unterstützt zudem das Konzept der Reflexion beziehungsweise der Metaprogrammierung. Die Sprache besitzt also die Fähigkeit, sich selbst zu analysieren. Ein einführendes Beispiel dazu gibt es auf GitHub.

Auch der Einsatz von Streams ist in Elixir möglich: Man verwendet sie, um Daten in eine Anwendung einzulesen oder sie aus einer Applikation auszugeben. Das zugrunde liegende Konzept ist dabei mit dem aus Java vergleichbar.

Geboten wird außerdem ein Pattern Matching, mit dem sich Daten nach einem String durchsuchen lassen.

Vor dem Einsatz

Um Elixir nutzen zu können, ist Erlang 17.0 (oder höher) nötig. Die Sprache unterstützt Linux-, Mac-OS-X- und Windows-Systeme. Unter Windows lässt sich der Web Installer zur Installation verwenden. Auf Linux-Systemen gibt es die bekannten Installationsvarianten. Hier der Weg für Ubuntu und Debian:

wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
&& sudo dpkg -i erlang-solutions_1.0_all.deb
sudo apt-get update
sudo apt-get install elixir

Nach erfolgreicher Installation lässt sich die interaktive Shell iex beziehungsweise unter Windows iex.bat starten. Wer mit Ruby arbeitet, wird sich schnell zu Hause fühlen, schließlich erinnert iex stark an irb. Das Tool bietet eine umfangreiche Dokumentation. Will man sich etwa die Beschreibung des Enum-Modul ansehen, ist h(Enum) einzugeben.

Mit Strg+C lässt sich das BREAK-Menü öffnen, über das Nutzer die Shell beenden und Einblick in laufende Prozesse nehmen können. Wichtig ist darüber hinaus die Tastenkombination Strg+G. Mit dem Menü, dass sich so öffnen lässt, kann man unter anderem eine zusätzliche Shell bereitstellen.

User switch command 
--> s 'Elixir.IEx'
--> c

Personalisiert wird die Shell über h IEx.configure/1. Die Konfiguration kann man dabei für das aktuelle Projekt oder global anpassen.

Gemeinschaftlich entwickeln

Rings um Elixir hat sich eine kleine, aber aktive Community etabliert, die mit ihren selbst entwickelten Tools und Anwendungen die Arbeit mit der Sprache komfortabler macht. Hervorzuheben ist vor allem der Paketmanager hex.pm, dank dessen sich Erlang- und Elixir-Code direkt als Pakete installieren lässt. Interessant ist zudem die Template-Engine EEx, die das Kompilieren von Templates zu Code ermöglicht. Auch bei ihr wird übrigens wieder die Nähe von Elixir und Ruby deutlich, da sich EEx am bekannten Ruby ERB orientiert:

<%= @content %>

Wie der Codeschnipsel zeigt, arbeitet EEx mit der üblichen Platzhaltersyntax. Um bei der Fehlersuche nicht auf den Komfort anderer Sprachen verzichten zu müssen, existiert mit ExUnit ein Test-Framework. Der Einsatz folgt dem folgenden Muster:

ExUnit.start

defmodule TestCase do
use ExUnit.Case

test "the truth" do
assert true
end
end

Abschließend ist auch für Elixir ein Build Tool erhältlich: mix ist das native Werkzeug der Wahl, wenn es darum geht, Projekte zu kompilieren.

Um sich über aktuelle Entwicklungen aus dem Elixir-Umfeld auf dem Laufenden zu halten, gibt es mittlerweile unter anderem eine Mailing-Liste, einen Twitter-Account und regelmäßige Veranstaltungen, über die die Community via GitHub informiert.

Syntax, Kontrollstrukturen, Maps

Besonderheiten der Syntax

Von Anfang an war das Ziel des Elixir-Erfinders José Valim ambitioniert, schließlich wollte er alles Positive von Erlang nutzen, ohne dabei auf die Vorteile anderer funktionaler Sprachen zu verzichten. Gleichzeitig sollten die bekannten Nachteile nicht mit in die Syntax einfließen. Da verwundert es wenig, dass sich Elixir syntaktisch in großen Teilen an Ruby orientiert. Eine interessante Analyse dazu ist in einem Blog-Eintrag unter dem Titel "Closures: Elixir vs. Ruby vs. JavaScript" zu finden.

Die gewisse Verwandtschaft zu Ruby ist beispielsweise darin zu sehen, dass sich auch in Elixir Threads definieren lassen. Ebenfalls an Ruby angelehnt ist die Möglichkeit zum Generieren von Code über die sogenannte Metaprogrammierung. Beide Aspekte sind so übrigens nicht nur in Ruby anzutreffen, sondern könnten ebenso Java entnommen sein. Aber nicht nur sie standen Elixir Pate: So wurde von PHP beispielsweise das Konzept übernommen, Code schreiben zu können, der – als eigener Prozess ausgeführt – eine Response zum aktuellen Prozess generiert. Anlehnungen gibt es darüber hinaus auch an NodeJS. Beispielsweise besteht die Möglichkeit, mit Nachrichten asynchron über Sockets zu arbeiten.

Obwohl die größte Schnittmenge mit Erlang existiert, lauern beim Umstieg auf Elixir dennoch einige Syntax-Stolpersteine. Das beginnt schon bei den Operatoren: Die in Erlang verfügbaren and- und or-Operatoren etwa gibt es in Elixir überhaupt nicht. Zudem haben die Entwickler andalso und orelse durch and und or ersetzt.

Unterschiede existieren auch hinsichtlich der Variablennamen. In Erlang lassen sich Variablen explizit nur einmal vergeben. In der Erlang-Shell gibt es mit f ein spezielles Kommando, mit dem sich Variablenbindungen aufheben lassen. Im Gegensatz dazu ermöglicht Elixir die mehrmalige Vergabe einer Variablen.

iex> a = 1
1
iex> a = 2
2
iex> ^a = 3

** (MatchError) no match of right hand side value: 3

Ein Blick auf die unterschiedlichen Arten der Funktionsaufrufe lohnt ebenfalls. Die Erlang-Variante einer Summenbildung:

sum()    

sieht in Elixir folgendermaßen aus:

sum

Beim Einsatz von Funktionsparametern gibt es zwischen Erlang:

sum(a,b)

und Elixir:

sum a, b

ebenfalls Differenzen. Die Syntax wirkt bei Erlang zugegebenermaßen ungewohnt.

Einen vollständigen Überblick der Syntaxunterschiede liefert die offizielle Elixir-Dokumentation.

Unter Kontrolle

Selbstverständlich stehen in Elixir Kontrollstrukturen zur Verfügung. Dabei handelt es sich um if, case und cound. Unterstützt werden zudem zahlreiche Expressions, unter die beispielsweise

  • logische Operatoren wie ==, !=, > und so weiter,
  • die booleschen Operatoren and, or und not
  • sowie die arithmetische Operatoren +, -, *, /

fallen.

Zusätzlich gibt es etliche Typprüfungsfunktionen wie is_atom/1, is_binary/1, is_bitstring/1, is_boolean/1, is_float/1, is_function/1 und is_integer/1:

iex> is_boolean(true) 
true
iex> is_boolean(1)
false

Aber auch Funktionen wie abs(number), binary_part(binary, start, length), bit_size(bitstring), byte_size(bitstring), div(integer, integer) und elem(tuple, n) sind in der Sprache zu finden:

iex> <<0, 1, 2, 3>> 
<<0, 1, 2, 3>>
iex> byte_size <<0, 1, 2, 3>>
4

Das if-Konstrukt ist dabei immer interessant, um eine Bedingung zu überprüfen:

if lang == :PHP 
IO.puts "Ich bin PHP-Profi"
end

Um auf unterschiedliche Bedingungen reagieren zu können, lassen sich zudem else-Blöcke nutzen:

if lang == :PHP 
IO.puts "Ich bin PHP-Profi"
else
IO.puts "Ich mag eine andere Sprache" end

Eine weitere Möglichkeit, um Bedingungen zu kontrollieren, ist das cond do:

cond do
language == :php and not is_javascript ->
IO.puts "Ich bin PHP-Experte"
language == :php and is_javascript ->
IO.puts "Ich bin PHP- und JavaScript-Experte"
language == :SASS ->
IO.puts "Ich kann nur SASS"
true ->
IO.puts "Ich kann vielleicht alles"
end

Maps und Sprachkombinationen

Ein interessantes Sprach-Feature sind die sogenannten Maps. Sie stellen nichts anderes als Datenstrukturen dar, bei denen Schlüssel auf Werte zeigen und auf die sich Pattern Matching anwenden lässt. Aber Achtung, hier schlägt Elixir ganz explizit einen anderen Weg als Erlang ein. Dort kommen Maps als Ersatz für Records zum Einsatz, in Elixir hingegen sind Records Tupel, die von Modulen unterstützt werden.

Erlang und Elixir lassen sich im Übrigen auch miteinander kombinieren. So könnte man beispielsweise Funktionen in Erlang schreiben und sie anschließend in Elixir laden. (Wie sinnvoll das letztlich ist, sei zunächst einmal dahingestellt.) Blake Williams hat sich dieser Option angenommen und sie in seinem Blog anhand einiger Beispiele dargestellt.

Phoenix, Fazit

Ein Blick in die Zukunft

Wie leistungsfähig Elixir bereits jetzt ist, zeigt das Projekt Phoenix. Das Web-Framework wurde vollständig auf Basis von Elixir geschrieben und ermöglicht unter anderem die Entwicklung von APIs und HTML5-Apps. Phoenix wurde in erster Linie entwickelt, um hochverfügbare und performante Web-Applikationen schreiben zu können. Voraussetzung für den Einsatz ist eine funktionierende Erlang-Umgebung, auf der Elixir läuft. Dort lässt sich das Phoenix-Archiv installieren.

$ mix archive.install https://github.com/phoenixframework/phoenix
/releases/download/v0.16.1/phoenix_new-0.16.1.ez

Standardmäßig wird PostgreSQL als Datenbankserver verwendet, der Einsatz von MySQL ist aber ebenfalls möglich. Dazu ist das Flag --database mysql beim Anlegen der gewünschten Applikation zu übergeben.

Auf Wunsch lässt sich in Phoenix auch Node.js einsetzen. Dafür ist allerdings mindestens Version 0.12.0 der serverseitigen JavaScript-Plattform nötig.

Phoenix steht, das zeigt auch die aktuelle Versionsnummer 0.16, noch ganz am Anfang seiner Entwicklung. Dennoch sind bereits jetzt mehr als nur ein paar gute Ansätze erkennbar. Die Entwicklung des Frameworks bleibt also spannend. Ausführliche Informationen zum Projekt liefert seine Webseite.

Fazit

Elixir wird, so scheint es zumindest derzeit, einer der entscheidenden Faktoren sein, wenn es um die Entwicklung von Anwendungen für das Internet der Dinge geht. Vor allem die hohe Verfügbarkeit ist es, die hier den Ausschlag geben dürften.

Elixir nutzt die Stärken von Erlang und erweitert diese geschickt. Für eine Programmiersprache ist sie mit gerade drei Jahren noch vergleichsweise jung, aktuell ist Version 1.0.5. Die kommenden Entwicklungen sollte man dennoch im Auge behalten, da sie die der Sprache weiteren Anschub geben dürften. (jul)

Daniel Koch
arbeitet als freiberuflicher Entwickler und Autor.