Transcript

Erlang/OTP

Eine Programmiersprache für non-stop laufende Systeme

In dieser Folge spricht Till Schulte-Coerne mit Christoph Iserlohn über Erlang, eine Programmiersprache und Laufzeitumgebung, die sich besonders zur Erstellung von non-stop laufenden Systemen eignet. Sie diskutieren dabei u. a. die Unterschiede zu anderen Programmiersprachen, die Besonderheiten der Laufzeitumgebung, das „ Let it crash“-Prinzip und was sich eigentlich hinter dem Kürzel OTP verbirgt.

Back to episode

Transcript

Christoph Iserlohn: Nein, das ist hier mein erster Podcast für die innoQ. Wie du gesagt hast, ich heiße Christoph Iserlohn. Ich bin jetzt etwas über drei Jahre bei der innoQ als Berater tätig. Mache da hauptsächlich Architektur für verteilte Systeme und auch die Implementierung. Im Moment mehrheitlich im Java-Umfeld, aber in meinem früheren Leben, da habe ich auch mal eine ganze Zeit lang viel Erlang programmiert und deshalb sprechen wir heute auch dadrüber. Und heute mache ich das immer noch ein bisschen und betreue auch bei MacPorts – einem Package-Manager für Mac OS X – das Erlang-Paket. Also ich glaube, ich kenne mich da einigermaßen aus.

Till Schulte-Coerne: Ja, dann lass uns mal direkt einsteigen. Erlang – Programmiersprache, ja. Was ist es überhaupt, wo kommt es überhaupt her? Kannst du uns da ein bisschen aufklären vielleicht zuallererst für die Leute, die noch nicht so viel damit zu tun hatten?

Christoph Iserlohn: Ja. Also Erlang ist wie gesagt eine Programmiersprache, aber nicht nur. Es ist auch die dazugehörige Laufzeitumgebung und auch eine – heute, worüber wir sprechen – eine ganz spezielle VM. Es gibt noch andere Implementierungen, z. B. auf der JVM, aber es gibt eigentlich keine, die sonst noch produktionsreif ist. Erlang kommt von Ericsson, dem Hersteller für Netzwerkausrüstung und -Equipment, und die haben das intern entwickelt, um eigentlich Telefon-Switche zu programmieren. Und das ist auch schon sehr alt, die Sprache. Die ersten Gehversuche damit sind aus den 80er-Jahren.

Till Schulte-Coerne: Aber, eine kurze Zwischenfrage: mit Cellphone-Switch meinst du jetzt nicht so ein Ding, was man mal eben auf einen Schreibtisch stellt, so wie ein Netzwerk-Switch, sondern das sind dann eher schon große Computer, oder?

Christoph Iserlohn: Das ist schon sowas, was in ein Rack gehört …

Till Schulte-Coerne: Genau.

Christoph Iserlohn: … und irgendwo draußen steht und in einem Verteilerhäuschen …

Till Schulte-Coerne: Ernst zu nehmende CPU haben.

Christoph Iserlohn: … und dann Tausende von Telefonaten oder Millionen von Telefonaten …

Till Schulte-Coerne: Es handelt sich nicht, das was ich meine, es handelt sich jetzt nicht um eine Programmiersprache, um embedded besonders toll entwickeln zu können, oder sowas. Sondern eben schon für ausgewachsene Systeme eigentlich.

Christoph Iserlohn: Für ausgewachsene Systeme. Es kommt ursprünglich da aus dem embedded Bereich, da kam es her. Also es ist eigene Hardware gewesen, aber jetzt ist es eigentlich für alle Größen geeignet. Man kann es auch nicht wirklich embedded nennen. Es waren wirklich schon große Dinger, damals in den 80ern. Was damals in den 80ern groß war … ähm ja.

Da kommt das her und wurde dann, ich glaube das erste Mal öffentlich vorgestellt 1988. Es wurde dann lange Zeit intern von Ericsson benutzt und auch an andere Firmen verkauft als proprietären Produkt. Und dann hatte man einen ganz tollen Switch gemacht, der sich unglaublich gut verkauft hat damit, wo man Riesenkosten gespart hat. Und dann hat sich Ericsson entschlossen, jetzt war das so gut, jetzt machen wir doch lieber alles in C++. Und die Leute, die das dann entwickelt haben, haben dann aber Ericsson überzeugt, dass man das als Open Source freigibt. Das war 1998, glaube ich. Also zehn Jahre danach hat man es dann als Open Source freigegeben und seitdem kann jeder die Sprache frei nutzen. Heute ist es noch so, dieser Bann wurde dann mal aufgehoben bei Ericsson, weil man dann doch irgendwann gemerkt hat, dass es eine schlechte Idee ist, alles in C++ zu machen. Und jetzt wird es zwar noch von Ericsson hoheitlich weiter entwickelt, aber es gibt halt ein Github-Repository und öffentliche Mailinglisten und es werden auch durchaus viele Patches von außerhalb angenommen. Aber die Richtung bestimmt immer noch Ericsson, wie es da weiter geht.

Till Schulte-Coerne: Und deiner Meinung nach die richtige Richtung?

Christoph Iserlohn: Ja. Also eine Steuerung finde ich bei so einem Projekt schon wichtig, dass es nicht irgendwelchen Wildwuchs entwickelt und jeder mit Tausend Ideen rein kommt. Es gibt da eine klare Vision, wo es hin gehen soll. Das macht natürlich manche Dinge ein bisschen schwerer. Also es gibt da auch einen relativ langen Release-Prozess für eine neue Erlang-Version. Bis man dann mit seinen Patches rein kommt, kann das was dauern. Also es ist jetzt nicht wie die hippe Node.js-Welt oder jetzt iOS.js, wo irgendwie jede Woche ein neues Release raus kommt. Es ist dann schon, man merkt, da ist eine große Firma hinter. Aber es ist jetzt nicht so schwerfällig. Also sowas wie bei Java, dass man dann auf eine Version jahrelang warten musste, sowas gab es noch nicht.

Till Schulte-Coerne: Und Closures gibt es vermutlich auch schon relativ lange.

Christoph Iserlohn: Ja.

Till Schulte-Coerne: Was uns eigentlich zum Thema bringt. Lass uns mal über die Sprache selbst ein bisschen reden. Warum eine neue Sprache? Was sind die Ziele gewesen? Wie kommt man auf die Idee? Ich mein, damals gab es noch nicht so viele wie heute, natürlich. Aber was ist das Ziel gewesen, warum haben die sich entschieden, was Eigenes zu machen?

Christoph Iserlohn: Also die Problemdomäne war damals, dass man Systeme bauen wollte, die a) sehr lange laufen mussten, ununterbrochen – also so ein Telefon-Switch kann nicht einfach mal ausfallen oder Downtime haben. Dann haben halt direkt, was weiß ich wie viele, hundert oder hunderttausend Kunden kein Telefon mehr – passiert ja heute noch, wenn man bei der Telekom mal sieht, dass das Voice over IP-Netz mal wieder bundesweit ausfällt. Das ist also ein NoGo.

Und das andere ist, es hatte die Wartungsproblematik. Also so ein Switch steht halt, was weiß ich, irgendwo in so einem Verteilerhäuschen in der Pampa. Das heißt, da kann man nicht einfach mal sagen “Naja, dann tauschen wir das mal eben aus.” oder so, wenn da Wartung ist. Also es musste halt irgendwie auch fehlertolerant weiterarbeiten. Fehler passieren, Hardware-Fehler passieren, Software-Fehler passieren und trotzdem musste das Ding dann halt weiterlaufen und Wartung musste dann während des laufenden Betriebs möglich sein. Das war die Domäne, auf der das hin entwickelt wurde. Deshalb hat man auch überhaupt eine neue Sprache entwickelt, denn dafür gab’s zu der Zeit einfach nichts. Also das mit dem normalen C oder C++, was es damals dann gab, konnte man das nicht realisieren und dann hat man halt angefangen zu sagen “Wir machen da was Eigenes.”

Ja, und dann hat man gesehen, man braucht so ein paar Prinzipien, wie man die Sprache designt, um diese Ziele zu erreichen. Und ich könnte die jetzt einfach mal aufzählen und dann gehen wir da mal vielleicht ins Detail danach. Das ist einmal, man braucht Nebenläufigkeit, und zwar sehr leichtgewichtig, weil Telefongespräche passieren halt gleichzeitig, und zwar jede Menge auf dem Ding. Und die müssen auch gleichzeitig behandelt werden können. Dann braucht man halt asynchrone Kommunikation dazwischen, also Nebenläufigkeit muss ich auch irgendwie wieder synchronisieren können an einer Stelle, also braucht man auch eine Kommunikation und die sollte asynchron sein. Alles sollte isoliert laufen, das ist halt klar. Wenn meine Telefonverbindung abbricht, möchte ich nicht, dass die anderen davon betroffen sind. Und das System muss halt sehr fehlertolerant sein, damit genau das nicht passiert, dass alles ausfällt. Und man hat sich dafür entschieden, damit man es auch lange pflegen kann, so ein Softwareprodukt, dass man eine relativ einfache Programmiersprache entwickelt, die aber ein hohes Abstraktionsniveau hat. Also damit man auch ein Softwareprodukt über Jahre pflegen kann und es nicht so ist, dass … Beim System, was halt jahrelang gepflegt ist, kommen Entwickler, gehen Entwickler und wenn es dann sehr kompliziert ist, ist es halt schwierig für die und die müssen so ein Softwareprodukt übernehmen können und da muss der Einstieg halt relativ leicht sein. Deshalb hat man gesagt, wir machen ein einfaches.

Till Schulte-Coerne: Fällt dir was vergleichbares ein? Was eine ähnliche – also Scala wäre es ja offensichtlich nicht.

Christoph Iserlohn: Also Scala ist der Gegenpol, C++ ist der Gegenpol. Ich finde C wäre vergleichbar, weil es sehr einfach ist, hat aber natürlich nicht die anderen Eigenschaften, die wir brauchen und ist natürlich sehr low-level mit eigenem Speicher-Management und Pointer-Arithmetik, die man da beherrschen muss, und da hat man natürlich das Problem, dass diese Fehlertoleranz damit extrem schwierig ist.

Till Schulte-Coerne: Wobei, ich weiß gar nicht, was sie in den Space Shuttles verbaut haben. Das war wahrscheinlich auch C.

Christoph Iserlohn: Oder Ada, vielleicht. Was dann natürlich nicht so komplex – was viel komplexer ist, meine ich

Till Schulte-Coerne: Die Problemdomäne ist ja nicht nur auf Telekommunikation beschränkt eigentlich, ne?

Christoph Iserlohn: Nee, aber beim Space Shuttle habe ich halt doch Hardware, die dann sehr beschränkt ist, während ich mit so einem Switch, auch für damalige Verhältnisse, halt schon Hardware habe, die auch eine höhere Programmiersprache verträgt. Also der Ressourcenmangel, dass ich C benutzen muss, ist halt nicht da.

Till Schulte-Coerne: Ja, wie sieht es denn dann aus? Das waren die Grundvoraussetzungen, jetzt gab es irgendwie ein Team, das sich da hingesetzt hat und versucht hat, darauf eine Antwort zu finden. Was war denn das Ergebnis?

Christoph Iserlohn: Genau. Also man hat dann erstmal die Sprache designt. Wir können jetzt mal einfach nur den seriellen Sprachteil behandeln und man hat eine funktionale Sprache gemacht. Das heißt, es gibt eigentlich nur zwei Abstraktionen: es gibt Module und es gibt Funktionen innerhalb der Module. Also es gibt keine Klassen oder sonst irgendwie was Prozeduren, sondern einfach so Funktionen, auch im Sinne von Funktionen, dass sie keine Seiteneffekte haben. Also eine einfache funktionale Sprache.

Dann, eine Besonderheit ist, dass die Syntax für viele Entwickler, die das zum ersten Mal sehen, sehr ungewöhnlich aussieht. Das liegt daran, weil sie sich an Prolog orientiert hat. Die ersten Implementierungen von Erlang, die jetzt im Labor entstanden, nur um mal damit zu spielen und zu sehen, wie es sein sollte, wurden nämlich in Prolog geschrieben. Und da hat man die Syntax übernommen. Das ist für den heutigen Entwickler oft sehr ungewöhnlich. Wer noch nie Prolog gesehen hat, der kennt halt vielleicht C, Java und die übliche Syntax. Ich glaube ALGOL-Familie nennt man das. Prolog sieht halt dann doch ein bisschen anders aus.

Till Schulte-Coerne: Wobei ja Relikte davon mittlerweile jetzt auch schon wieder überschwappen, ne? Also die Funktionsdeklarationssyntax mit diesem Pfeilchen findet man ja heute auch in relativ vielen Programmiersprachen wieder, plötzlich.

Christoph Iserlohn: Das findet man wieder. Aber so Konventionen, dass Variablen groß geschrieben werden müssen, ist eher ungewöhnlich, das hat kaum eine andere Sprache. Und dass so über Semikola die einzelnen Pattern abgegrenzt werden – zu Pattern kommen wir später noch –, das gibt es dann halt auch nicht.

Till Schulte-Coerne: Ja ja, das stimmt.

Christoph Iserlohn: Und sowas. Und dass was mit einem Punkt – also die Funktionsdefinition endet mit einem Punkt. So, das kennt man auch nicht. Es sieht halt anders aus, wenn man das zum ersten Mal sieht.

Till Schulte-Coerne: Ja, das stimmt.

Christoph Iserlohn: Man erkennt es aber noch als Programmiersprache.

Till Schulte-Coerne: Wenn ich versuche, Erlang zu machen, dann kriege ich das auch immer nicht hin.

Christoph Iserlohn: Ja, kann man sich halt drüber streiten, ob das schön ist oder nicht. Auf jeden Fall ist die Syntax nicht so besonders komplex.

Till Schulte-Coerne: Ja, das stimmt.

Christoph Iserlohn: So, also wenn man sie kann, es sind nicht so viele Elemente, die man lernen muss. Sieht halt nur anders aus. Gut, was kann man noch dazu sagen? Also es ist dynamisch typisiert. Und es gibt nur sehr wenige Datentypen. Also ich weiß nicht, zehn Stück vielleicht? Es gibt Zahlen beliebiger Größe, Gleitkommazahlen, was gibt es noch? Atome, das sind sowas wie Symbole in Ruby zum Beispiel oder einfach Konstanten. Also ein Atom steht für sich selbst. Oder Symbole in LISP wären was vergleichbares. Und dann gibt es halt noch Collection-Typen: es gibt Tupel, recht klar. Die können auch – müssen nicht homogen sein, da kann was beliebiges drin stehen. Und es gibt Listen als Collection-Typ.

Till Schulte-Coerne: Was ist mit Zeichenketten?

Christoph Iserlohn: Gibt’s halt nicht. Erstmal nativ. Es gibt in der Syntax Unterstützung für Zeichenketten, aber intern sind das einfach nur verkettete Listen. Da muss man sehr aufpassen, wenn man große Mengen davon hat, ist so eine verkettete Liste halt für einen String nicht so besonders gut, weil man braucht halt einen Pointer für auf das vorige Element, einen auf das nächste und ein Wort, wo das Zeichen drin steht. Dann hat man irgendwie, ich weiß nicht wie viel Bits dann verbraucht. 64 waren es mal, aber es sind jetzt, glaube ich, mehr. Also auf der 32-Bit Plattform waren es schon 64 Bit, die man dafür verbraucht hat. Jetzt sind es noch mehr. Also es ist nicht so besonders effizient. Es gibt aber noch einen Datentyp, den man dann dafür nimmt: es gibt sogenannte Binaries. Das ist einfach Raw-Bytes. So, die man sehr effizient behandeln kann. Und wenn ich große Strings habe, kann ich mit diesen Bytes arbeiten dabei. Das macht es dann nicht immer ganz einfach, wenn man so Encoding Sachen hat, ne? Ascii, UTF-8.

Till Schulte-Coerne: Wo ist das schon einfach?

Christoph Iserlohn: Ja, wo ist das schon einfach? Da ist Erlang jetzt nicht so besonders stark drin. Und es gibt noch sogenannte Bit-Strings. Das sind halt rohe Bits, ich kann auf der Bit-Ebene arbeiten. Das heißt, ich habe nicht diese Byte-Grenzen, immer so auf acht Bytes, sondern ich kann sagen “Ok, ich habe hier 17 Bytes und davon …”

Till Schulte-Coerne: Ich habe da auch relativ häufig schon einen anderen Erlang-Fanboy Kollegen von uns schwärmen hören, wie toll und wie einfach Bit-Manipulation und Bit-basiertes Arbeiten in Erlang ist. Das liegt dann eben an der Herkunft dieser Sprachen, ne?

Christoph Iserlohn: Ja, das liegt daran, dass die low-level Treiber für die Telefon-Hardware halt die Bytes und Bits liefern und keine höheren Datentypen. Und darauf hat man das dann angepasst, dass die als native Datentypen verfügbar sind und dann Operationen da sind, wo ich in Java irgendwelche Bit-Shift-Operationen machen muss auf irgendwelchen Ints, mir dann da mühsam was rauskopiere, wo ich in Erlang einfach sagen kann “Ok, die ersten drei Bits nimmst du dafür und die restlichen 18 Bits nimmst du dafür.” Und dann hat man 21 Bit, also auch eine krumme Zahl, mit der man sonst in anderen Programmiersprachen eher nicht umgehen kann.

Till Schulte-Coerne: Ja.

Christoph Iserlohn: Ja, was ich eben erwähnt habe, Pattern Matching, ist das typische Element, mit dem ich Programme strukturiere. Als Pattern Matching gibt es überall, ich kann auf Funktionsebene schon Pattern Matching machen. Also ich sage “Ich habe hier eine Funktion” und die Parameter deklariere ich nicht als Variablen, sondern ich sage “Ok, wenn der erste Parameter eine Eins ist, dann gehst du hier rein” und dann deklariere ich die Funktion nochmal und mache einen anderen Pattern in die Signatur und kann die dann ausführen lassen.

Till Schulte-Coerne: Das hat jetzt aber nichts mit regulären Ausdrücken oder sowas zu tun, sondern das ist ein eigenes Sprachkonzept, was man jetzt vielleicht aus Scala kennt, ne?

Christoph Iserlohn: Aus Scala kennt man es vielleicht.

Till Schulte-Coerne: Was letztenendes ansonsten, glaube ich, auch ein sehr neues Konzept ist, wenn man zu Erlang kommt, in den meisten Fällen, ne?

Christoph Iserlohn: Ja. Also das kommt jetzt mehr in den Mainstream in anderen Sprachen. Also in Scala zum Beispiel gibt es das. In anderen, in Clojure gibt es Bibliotheken dafür, die das dann nachimplementieren, so ein Pattern Matching. Aber bei denen ist das halt alles so, dass ich das irgendwo innen drin habe. Also bei Scala diese Case-Klassen, und dann kann ich darauf matchen. In Erlang kann ich halt in den Funktionskopf matchen, ich habe einen Case-Statement dadrin, bzw. einen Ausdruck, der vergleichbar ist mit dem, was es in Scala gibt, und ich kann aber auch einfach Variablen matchen. Also das ist das strukturierende Element.

Till Schulte-Coerne: Die Zuweisung einer Variablen ist eigentlich ein Match.

Christoph Iserlohn: Genau.

Till Schulte-Coerne: Und nur, um das nochmal so zu versuchen, ich glaube ein gutes Beispiel ist doch Rekursion, oder? Was man sehr elegant über Pattern Matching lösen kann.

Christoph Iserlohn: Ja, das ist das, was ich gerade erwähnt habe, dass man halt schon in der Funktionssignatur matcht und dann die Abbruchbedingung auch einer dieser Matches ist, dann brauche ich da nicht irgendeine große if-soundso if-soundso.

Till Schulte-Coerne: Also die leere Liste …

Christoph Iserlohn: Genau.

Till Schulte-Coerne: … wäre die erste Funktion, die ich definiere und die zweite wäre dann mit einer nicht-leeren Liste entsprechend.

Christoph Iserlohn: Ganz genau. Ich bearbeite eine Liste rekursiv und das heißt, es gibt zwei Klauseln, nennt man das – Funktionsklauseln –, und die erste, genau, in der ersten steht “Wenn es die leere Liste ist, dann gibst du einfach nur deinen – also man hat dann leere Liste und irgendeinen Akkumulator, heißt das meistens, der Variablenname, wo das Ergebnis drin steht – dann gibst du einfach nur den Akkumulator zurück.” Und bei dem anderen hat man dann eine, matcht man dann gar nicht auf die leere Liste, sondern sagt dann “Das ist jetzt Variable X und der Akkumulator”, macht dann die Bearbeitung, schreibt dann das Ergebnis in den Akkumulator und ruft sich wieder selbst auf damit. Und irgendwann ist die Liste halt leer. Und da hast du auch eine Besonderheit erwähnt. Es gibt auch in Erlang keine Schleifenkonstrukte. Es wird alles über Rekursion gelöst. Es gibt kein while, kein do, kein for, sondern es gibt nur Rekursion. Das ist die einzige Möglichkeit, eine Schleife zu implementieren. Und deshalb ist Erlang, wie heißt das so schön, endrekursiv. Das heißt, wenn die letzte Instruktion bei einer rekursiven Funktion jetzt keine Daten auf dem Stack übrig lässt, dann wird der abgeräumt, dann wird das intern in einen Jump umgewandelt und – also ich bekomme keinen Stack Overflow, ist das Ergebnis.

Till Schulte-Coerne: Ok.

Christoph Iserlohn: Bei Java, wenn ich das mache, habe ich irgendwann das Stack-Limit erreicht – oder auch bei vielen anderen Programmiersprachen – weil bei jedem Funktionsaufruf wird der neue Stack-Frame erzeugt und da steht was drauf und das kann Erlang halt wegoptimieren. Sonst würde das nicht funktionieren ohne Schleifen.

Till Schulte-Coerne: Aber auch gewöhnungsbedürftig wahrscheinlich, aber eigentlich sehr komfortabel.

Christoph Iserlohn: Das ist gewöhnungsbedürftig. Andere Programmiersprachen gehen da andere Wege, in Python will man es genau umgekehrt machen, alles explizit über Schleifen und Rekursion ist doof. In Erlang sagt man “Rekursion ist das einzig Wahre!”

Till Schulte-Coerne: Und das letzte, was noch seltsam anmutet, glaube ich, ist das Thema Variablen, ne? Also das gibt es, glaube ich, gar nicht so richtig.

Christoph Iserlohn: Nein, also eigentlich, es gibt sie schon. Das sind sogenannte Single-Assignment-Variablen. Das heißt, wenn ich die einmal zugewiesen habe, kann ich den Wert einer Variablen nicht ändern. Und eigentlich ist es auch kein Assignment, sondern ein Matching. Und wenn ich matche und diese Variable gibt es noch nicht, dann ist dieser Match immer true. Und dann hat man ein sogenannte Assignment geschaffen. Also sagen wir mal, wenn ich zwei Zeilen habe, X = 1 und in der nächsten Zeile ist X = 2. Bei der ersten gibt es X noch nicht, dann wird gematcht und dann ist, X hat dann den Wert eins. Und danach habe ich kein Assignment mehr, sondern dann steht da 1 = 2 als Match und dann sagt er “Bad Match” und steigt aus. Das funktioniert dann halt nicht mehr. Und das kann ich dann wieder abfangen oder was mit machen, aber naja.

Till Schulte-Coerne: Auch eher gewöhnungsbedürftig.

Christoph Iserlohn: Es ist gewöhnungsbedürftig, dann gibt es halt so lustige Sachen, dass man im Programm sieht, wo nachträglich Variablen eingefügt werden, die also keine sprechenden Namen haben, dass dann Leute schreiben X10 und X20, also wenn der Wert verändert wird, damit sie noch dazwischen, wenn sie mal einen Zwischenschritt brauchen, X15 einfügen können, weil sie für jeden Zwischenschritt halt eigentlich einen neuen Variablenamen brauchen. Das ist kein guter Stil, eigentlich sollte man direkt sprechende Namen geben, dann hat man das Problem nicht.

Till Schulte-Coerne: Wie so häufig.

Christoph Iserlohn: Aber ab und zu sieht man halt in der freien Wildbahn solche Konstrukte.

Till Schulte-Coerne: Ja, ich meine, wir haben jetzt eine funktionale Sprache. Ein bisschen gewöhnungsbedürftige Syntax, was vielleicht auch auf das Entstehungsdatum zurückzuführen ist, so ein bisschen? Heute würde man wahrscheinlich sich an irgendwas anderes anlehnen. Aber nichtsdestotrotz, eben hast du noch von Isolation und Asynchronität geredet, tja, ich meine klar, nur weil irgendetwas funktional ist, geht das leichter vielleicht, aber das erbringt das ja nicht. Also was kommt jetzt noch dazu? Also, was macht es aus?

Christoph Iserlohn: Ja. Das ist halt das Feature, dass es in Erlang Prozesse gibt. Also vorher hatten wir den seriellen Teil, nur die Sprache besprochen. Jetzt gibt es halt noch diese Prozesse. Andere nennen die auch Aktoren. In der Erlang-Community ist es etwas umstritten, die Sprachregelung. Da redet man dann halt davon, dass es Prozesse sind und keine Aktoren und hat dann noch ganz spezielle Begründungen, warum das keine Aktoren sind. Da müssen wir jetzt nicht drauf eingehen. Auf jeden Fall starte ich halt, kann ich in Erlang x-Tausend Prozesse starten. Prozesse sind Userspace-Prozesse, es sind keine Betriebssystemprozesse und sie sind dadurch sehr preiswert. Also sie belegen wenig Speicherplatz und auch ein Kontextwechsel ist sehr leicht möglich. Das heißt, ich benutze die Prozesse für eine ganze Menge von Funktionen, die ich haben will. Also einmal für die Nebenläufigkeit – so ein Telefon-Switch muss halt ein paar Tausend Telefonate gleichzeitig behandeln können. Das heißt, da kriegt jedes Telefonat einfach einen eigenen Prozess. Und es wird zur Parallelisierung benutzt. Diese Prozesse können, obwohl sie Userspace-Prozesse sind, parallel ausgeführt werden. Also die Erlang-VM hat einen Scheduler, der parallel arbeitet. Und sie sind dafür benutzt worden oder werden dafür benutzt, um die einzelnen Teile eines Systems voneinander zu isolieren. Also da gleichen sie halt Betriebssystemprozessen. Wenn ein Prozess abstürzt, dann hat das erstmal keine Auswirkung auf die anderen. Jeder hat seine eigenen Daten, hat seinen eigenen Stack, also alles, was er braucht, hat er selbst.

Till Schulte-Coerne: Um diese Isolation zu betonen, benutzt man wahrscheinlich auch den Begriff Prozess dann so gerne, ne?

Christoph Iserlohn: Vielleicht.

Till Schulte-Coerne: Weil es halt wirklich komplett isoliert, eigentlich von allem anderen, ist.

Christoph Iserlohn: Genau. Es ist halt sehr ähnlich eines Betriebssystemprozesses.

Till Schulte-Coerne: Was mich zum Beispiel sehr beeindruckt hat, war, dass, glaube ich, Whatsapp auf Erlang läuft, das Backend.

Christoph Iserlohn: Ja.

Till Schulte-Coerne: Und die werden wahrscheinlich einige zig Millionen parallele Verbindungen haben. Ich meine, da gäbe es auch einen Eintrag, einen Blog-Beitrag irgendwo zu. Ich kann mal gucken, ob ich den noch finde. Und das läuft letzten Endes auf ganz wenigen Maschinen. Also, ganz wenigen Erlang-Maschinen, die zig Millionen Prozesse abkönnen, gleichzeitig. Das ist schon ziemlich …

Christoph Iserlohn: Also vielleicht mal, um davon eine Vorstellung zu geben, auf dem Raspberry Pi, das ist jetzt nicht so Hardware-stark, kann ich durchaus über 200.000 Prozesse starten. Und das System läuft noch ohne weiteres. Und irgendwann kommt man da halt in eine Beschränkung für den Speicher, der hat halt nur 512 MB, wo auch noch ein bisschen Betriebssystem drauf läuft und da was weg nimmt. Das ist schon sehr beeindruckend. Auf meinem Laptop kriege ich – es ist schon ein älteres Notebook, irgendwie schon drei Jahre alt – kriege ich auch über eine Millionen hin. Wenn ich dann so Server-Hardware habe und einen entsprechenden Rahmen, dann kriege ich das halt, dann hat auch Whatsapp keine Probleme das auf relativ wenig Servern laufen zu lassen. Und da ist es, soweit ich das so gehört habe, so, dass halt ein so ein Chat, nenne ich das jetzt mal, eine Gruppe, ein persönlicher Chat halt auch jeweils durch einen Prozess abgebildet wird.

Man kann sich das vorstellen, im Web-Server ist das halt so, wenn ich jetzt einen Erlang Web-Server habe, dass auch jede Verbindung, einen Prozess hat. Manchmal sogar mehrere. Es gibt dann halt einen Prozess, der einfach nur einen Input-Output macht und einen Prozess, der den Request verarbeitet. Das heißt, ich habe zwei Prozesse für einen Request. Und wenn ich das jetzt mit einem Apache mache, der jetzt Betriebssystemprozesse nimmt, dann bin ich bei 10.000 an der Schmerzgrenze. Dann gibt es halt den Yaws Webserver, da gab es mal einen Vergleich zwischen Yaws und Apache. Yaws – Yet another web server – ist halt ein ganz gutes Produkt, was es schon sehr lange in der Erlang-Welt gibt, da kann ich halt Millionen Connection mit abhandeln, ohne dass der da ins Schwitzen kommt, weil diese Prozesse halt so leichtgewichtig sind. Die belegen halt ein paar Byte für ihre Daten und gut ist.

Till Schulte-Coerne: Aber jetzt habe ich die komplett getrennt, also jetzt habe ich zwei Millionen Dinge parallel gestartet, jetzt muss ich die doch auch irgendwie wieder zusammen kriegen, eigentlich, oder?

Christoph Iserlohn: Ja, die müssen halt auch miteinander kommunizieren, ne? Sonst kriegt man wahrscheinlich kein sinnvolles System programmiert. Und dafür gibt es halt asynchrone Kommunikation über Message Passing. Das heißt, jeder Prozess hat auch eine sogenannte Mailbox assoziiert und dem kann ich was schicken. Also entweder kenne ich seine Prozess-ID oder ich kann Prozesse auch mit Namen registrieren und kann die dann per Namen ansprechen und dem kann ich was schicken. Und da gibt es einfach nur zwei relativ einfache, primitive Funktionen auf denen die gesamte Kommunikation aufbaut. Das ist einmal das sogenannte “Fire and Forget”. Also, man schickt seine Nachricht ab, aber man hat keine Garantie, ob die ankommt und wann die ankommt. So. Das ist in lokalen Systemen erstmal, kann man davon ausgehen, dass sie zu 99,9% immer ankommt und zwar instantan. Aber Erlang hat auch eine Verteilung, auf die wir vielleicht später kommen, das heißt, wenn es über Netzwerk geht und über Computergrenzen hinaus, dann ist es natürlich nicht mehr garantiert. Und das andere ist das “Selective Receive”. Das heißt, ich kann auf der Mailbox ein receive aufrufen und dann kriege ich Nachrichten. Und dieses “Selective” heißt, ich kann ein Pattern mitgeben und dann halt nur Nachrichten, die diesem Pattern entsprechen, auch da raus holen. Das ist – nochmal im Rückgriff auf “Was ist ein Prozess, Was ist ein Aktor?” – ein Unterschied. Normale Aktoren müssen halt alles Eingehende irgendwie bearbeiten. Erlang kann sagen “Ja, interessiert mich jetzt erstmal nicht.” So kann ich z. B. eine Reihenfolge sicherstellen. Wenn jetzt Nachrichten in einer bestimmten Reihenfolge kommen müssen, weil ich ein Protokoll habe, dann kann ich natürlich mein Matching erstmal auf die Erste machen. Und dann muss ich mich drum kümmern, ob die jetzt wirklich in der Reihenfolge bei mir ankommen. Man kann dann sagen “Wenn ich das Receive 1 habe und das matcht, der ist da drin, dann kommt innendrin geschachtelt das Receive 2 auf die zweite Nachricht, die in meinem Protokoll vorgesehen sind.” Dann brauche ich da nicht groß umsortieren.

Till Schulte-Coerne: Setzt natürlich voraus, dass das die zweite Nachricht auch schon da ist.

Christoph Iserlohn: Ja. Receive blockiert erstmal, bis es matcht, bis eine Nachricht kommt. Ich kann aber auch sagen, nach soundso viel Millisekunden gibst du dann auf.

Till Schulte-Coerne: Ok, gibst du auf. Ok. Und das ist etwas, was ein Erlang-Jünger auch jederzeit zu tun bereit ist, eigentlich, ne?

Christoph Iserlohn: Muss man, weil sonst kriege ich natürlich ein Problem. Wenn ich, sagen wir mal, ich warte auf die erste Nachricht und die kommt aber nie, aus irgendeinem Grund, weil irgendwo anders ist der Prozess abgestürzt und kann mir die nicht schicken. Und von anderen Seiten kommen halt x andere Nachrichten. Dann platzt mir natürlich auch irgendwann die Mailbox. Die werden ja trotzdem erstmal angenommen. Das heißt, dass man immer mit Timeouts arbeitet, ist da eigentlich Standard. Sonst hilft auch Erlang nicht dafür, ein System zu bauen, was ewig läuft. Mit diesen Sachen muss man umgehen. Aber es ist halt einfach auch in der Syntax veranlagt. Also ich muss nicht irgendwie …

Till Schulte-Coerne: Das meinte ich.

Christoph Iserlohn: … wie in Java einen Thread aufmachen, der dann einen Timer läuft und der mich wieder benachrichtigt, sondern ich sage einfach receive <Pattern> dann kommt der Block, wo ich die Nachricht verarbeite, dann kommt after <Anzahl der Millisekunden> und das heißt, wenn es da einen Timeout gibt, dann machste halt das. Das ist halt direkt drin.

Till Schulte-Coerne: Das ist das, was ich damit meine, eigentlich, ne? Also in Erlang ist es absolut gewöhnlich und gewollt eigentlich, dass man relativ schnell auf Fehler reagiert und dann eben entsprechend über seine Plattform letztenendes die Möglichkeit bekommt, damit dann auch umzugehen, ne? Und nicht so zu tun, als gäbe es keinen Fehler.

Christoph Iserlohn: Genau und deshalb ist das halt auch direkt in die Sprache eingebaut, solche Konstrukte. Und auf diesen beiden Primitiven “Fire and Forget” und “Send and Selective Receive” kann ich halt größere, komplexe Kommunikationsprotokolle aufbauen. Mehr brauche ich eigentlich nicht. Also das ist ganz gut. Und ja, so kann ich dann halt Nachrichten hin und her schicken und gleichzeitig impliziert das, dass ich damit natürlich keinen globalen State irgendwo habe. Jeder Prozess hat das und der schickt was und das ist eine Kopie, also die Nachricht ist immer eine Kopie der Daten. Ich kann also nicht in einem anderen Prozess Daten manipulieren. Ich kann dem eine Nachricht schicken “Jetzt mach mal das” und dann kann er seinen internen State vielleicht verändern, den er da hält, seine internen Daten. Aber die müsste ich dann auch wieder neu abfragen oder es kriegt jemand eine Antwort.

Till Schulte-Coerne: Würde ja sonst auch das Aktoren-Modell völlig ad absurdum führen, ne? Eigentlich, wenn man die Daten, die man hin und her schickt, manipulieren kann von zwei Seiten.

Christoph Iserlohn: Ja, bei den Aktoren schon, aber wenn man jetzt mal einen kleinen Schwenk macht zu Go, da geht das ja durchaus. Da kann ich ja auch einen Pointer auf irgendeine Datenstruktur schicken, also bzw. diese Go-Routines, die ähnlich sind und die über Channels kommunizieren. Dann kann ich dem Channel eine Nachricht schicken und diese Go-Routine nimmt das auf und in der Nachricht ist aber ein Pointer drin auf irgendeine andere Datenstruktur. Das geht durchaus.

Till Schulte-Coerne: Aber dann bräuchte ich halt andere Sprachkonstrukte.

Christoph Iserlohn: Ja oder ich muss mehr Disziplin haben, ne? Oder gehe einfach davon aus, dass ich auch mal crashen kann, ne? Kann ja auch ok sein.

Till Schulte-Coerne: Ja.

Christoph Iserlohn: Ja.

Till Schulte-Coerne: Das bringt uns glaube ich auch zum letzten Punkt, den wir bezüglich der Sprache noch aufgeschrieben hatten. Ich hatte es eben schon so ein bisschen angedeutet. Der Umgang mit Fehlern ist halt in Erlang …

Christoph Iserlohn: Genau.

Till Schulte-Coerne: … sehr speziell, sage ich mal.

Christoph Iserlohn: Ja, es fährt eine ganz andere Philosopie, als alle mir bekannten Sprachen. Das Motto ist “Let it crash”. Und damit ist jetzt nicht Windows gemeint, der Bluescreen, sondern Erlang sagt oder Erlang hat das Prinzip, dass man sagt “Ok, ich programmiere nur für den guten Fall.” Also ich habe jetzt nicht, wie in C oder in Go, if-Kaskaden, die je nach Funktionsaufruf “Oh, ist da was passiert? Ist das gut gegangen? Ist das gut gegangen?” Oder in Java dann irgendwelche Try-Catch-Konstrukte, habe ich halt nicht, sondern die sagen, es wird so programmiert, die reine Logik, das ist der gute Fall und wenn was passiert, dann stirbt der Prozess halt. Da kümmert sich jemand anderes drum, ne? Das macht den Code sehr schön lesbar, also wenn ich jetzt C lese, habe ich halt irgendwie 50–80% Fehlerbehandlungscode. Bei Java etwas weniger, weil die Exceptions halt viel weiter höher abgerufen werden können, aber da habe ich auch viel Fehlerbehandlungscode. Und da habe ich halt eigentlich so gut wie gar keinen, sondern ich sage “Ok, ich mache das und wenn es nicht geht, dann Crash.” Das heißt natürlich, dass sich auch jemand drum kümmern muss. Und dafür gibt es auch Konstrukte. Es gibt links und monitors. Das heißt, ich kann Prozesse verlinken oder monitoren. Das eine ist halt unidirektional, das andere bidirektional. Und wenn ich jetzt so einen Prozess verlinke, Prozess A mit B verlinke und B stürzt ab, dann kriegt A eine Nachricht in seine Mailbox “Prozess soundso ist exited. Crash reason …” Also dann kann da das schauen und kann dann sehen, was ich mache.

Till Schulte-Coerne: Aber delegiere ich jetzt nicht einfach nur die ganze Komplexität? Dann entsprechend, also wenn ich jetzt hier irgendwelche Links und Monitore habe, dann bedeutet das doch, dass ich das dann auch wieder implementieren muss und dann eben entsprechend auch wissen muss, wie ich denn da mit den Fehlern umgehe, oder?

Christoph Iserlohn: Natürlich muss ich wissen, wie ich mit einem Fehler umgehe, klar. Das ist meistens ja auch fach- oder anwendungsspezifisch. Aber die Strategie dahinter ist, dass ich sage “Ok, da kommt jetzt ein Fehler.” Und ich habe jetzt verschiedene Möglichkeiten, was ich mache. Ich kann z. B. den Prozess neu starten und sagen “Mach das nochmal.” Ich kann sagen “Ok, wenn Prozess A kaputt gegangen ist, dann können auch B und C nicht mehr weiterarbeiten, dann schieße ich die einfach auch ab und starte diese ganze Gruppe nochmal neu.” Und man kommt dann irgendwann auf so einen schönen Baum, wo irgendwo oben Prozesse da sind, die nur dafür da sind, andere Prozesse zu überwachen. Und die haben dann eine Strategie, wie sie damit umgehen. Also es gibt dann oben – das nennt man den Supervisor und der überwacht dann fünf Prozesse, die irgendeine Arbeit verrichten, eine fachliche Arbeit, irgendwelche Worker. Und dann geht einer davon in die Binsen, ist kaputt, und dann kann der Supervisor entscheiden “Ok, ich starte den halt neu.” Oder “Ich starte alle einfach mal neu.” Oder “Ich beende mich selber.” Weil oft ist es so, dass es so eine ganze Hierarchie ist, dass also ein Supervisor auch nochmal wieder von einem anderen Supervisor überwacht wird, weil mein ganzes Sub-System ist kaputt. Also Prozesse sind dafür zuständig, mit irgendeinem externen System zu reden. Das ist aber nicht da, Netzwerk ist ausgefallen oder es ist nicht da. Dann kann ich sowieso nichts machen, dann kann der Supervisor sich beenden und sagen “Ich bin jetzt gerade weg, Sub-System ist weg, geht nicht.” Und so ist die Behandlung. Und da kann man sehr ausgefeilte Strategien machen, also ich kann dann halt sagen “Ok, ich starte jetzt einen Prozess neu, aber in fünf Sekunden darf der mir maximal drei Mal abstürzen, dann höre ich halt auf. Dann ist es ein permanenter Fehler und ich mache da nichts mehr.” Und das kann man sehr ausgefeilt steuern. Dafür gibt es Hilfsmittel im OTP-Bereich, da kommen wir später zu, die einem dann schon das Leben erleichtern, dass man solche Strategien nicht halt alle selber schreiben muss. Und das macht es halt sehr elegant. Man hat die Fehlerbehandlung getrennt von dem fachlichen Code. Sehr gut, also es macht das sehr lesbar und ich habe einen ganz klaren Schnitt, was man dann in anderen z. B. dann eben irgendwie über AOP erreichen will, ne? Dass man sagt, solche Querschnittsthemen, die lagere ich irgendwie aus. Das habe ich da halt eingebaut, indem ich Prozesse einfach verlinke und in eine große Baumstruktur – Supervison Trees nennt man die dann, ist halt ein Wort – wo ich dann halt einen Ober-Supervisor habe für die Applikation. Der startet seine Sub-Systeme, die er dafür braucht, die haben auch wieder einen Supervisor. Und die startet irgendwo unten in der Ebene und müssen dann irgendwelche Worker-Prozesse, die die eigentliche Arbeit machen, dann überwachen.

Nochmal vielleicht auf dieses Webserver-Beispiel, wo ich dann zwei Prozesse habe, die miteinander kommunizieren. Da habe ich auch einen Supervisor drauf. Eigentlich habe ich sogar für so einen Request drei Prozesse. Und der eine ist jetzt für I/O zuständig und der kann jetzt nicht mehr – Netzwerkverbindung ist kaputt. Dann hat es keinen Sinn, den Prozess, der den Request eigentlich verarbeitet, noch weiterlaufen zu lassen. Dann würde ich beide Prozesse einfach killen. Request ist gescheitert, kille ich beide, gut ist. Einfachste Fehlerbehandlung. So, wenn jetzt der andere Prozess stirbt, der die fachliche Verarbeitung macht, dann macht es natürlich Sinn, den starte ich nochmal neu und der soll das halt nochmal probieren, vielleicht. Ne?

Till Schulte-Coerne: Ja.

Christoph Iserlohn: Und so funktioniert die Fehlerbehandlung, das ist halt das Motto “Let it crash.”

Till Schulte-Coerne: Genau.

Christoph Iserlohn: Und das funktioniert sogar sehr gut.

Till Schulte-Coerne: Aber ich stelle mir das immer noch relativ schwierig vor. Also du hattest OTP ja schon mal erwähnt, irgendwie. Ich stelle mir das schon schwierig vor, jetzt diese ganzen Supervisor zu programmieren. Das ist jetzt ja offensichtlich irgendwie kein Sprachumfang mehr, wie geht man den jetzt damit um, im täglichen Leben? Also geht jeder dann hin und programmiert das selbst oder was macht man dann?

Christoph Iserlohn: Nein, das wäre ein bisschen aufwendig, da immer wieder das Rad da neu zu erfinden, weil also diese Strategien, da gibt es halt ein paar feste, die man so benutzen kann. Die kann man dann halt tunen, ne? Man kann dann sagen, nach wie viel Sekunden soll es ein permanenter Fehler sein, wie oft soll der den überhaupt neu starten, also in welcher Frequenz, ne? Damit ich kein Overload z. B. erzeuge, weil ich sage “Ok, irgendwas geht schief, alle Prozesse sind tot und ich starte alle neu.” Und der andere, der da abhängig ist, der wird dann völlig überladen, weil alle neuen Prozesse da rein ballern. Dann kann ich auch sagen “Ok, mach erstmal so ein langsames Ramp up.” Das sind so ein paar Strategien, die immer gleich sind.

Till Schulte-Coerne: Das ist ja gerade sehr, sehr hip, ne? Also durch …

Christoph Iserlohn: Ja, also …

Till Schulte-Coerne: … “Release it” von Michael Nygard, ist ja jetzt schon ein paar Tage alt, aber jetzt gerade wieder durch Hystrix, da können die Erlanger nur müde drüber lächeln? Oder wie sieht das aus?

Christoph Iserlohn: Die haben das Pattern jetzt nicht so benannt, aber die haben das schon lange drin, machen das schon lange. Genauso, weil sie halt Systeme brauchen, die halt 24/7 jahrelang laufen müssen, haben die sowas drin, ne? Und wenn ein Sub-System kaputt ist, dann geht halt auch der Rest weiter. Genau das ist das. Das ist halt in der Sprache gut unterstützt. Also, was ich gerade gesagt habe, diese Links der Prozesse, aber ich programmiere das jetzt nicht immer alles neu, da gibt es halt dieses OTP – das ist die Open Telecom Platform, wobei heutzutage nichts mehr von Telecom mehr da drin ist, das kommt halt aus der Geschichte, dass es so heißt. Und das sind halt, das ist eine ganze Menge. Das sind Tools, die da drin sind, für die Erlang-Plattform. Da sind Bibliotheken, die ich benutzen kann. Und da sind Design-Patterns oder Blueprints, die ich da drin habe. Und so ein Supervision Tree ist z. B. so ein Blueprint. Das sagt da “Ok, so wird die Fehlerbehandlung gemacht und man kann das mit folgenden Strategien machen.” Das ist halt das, was der Nygard dann auch mal beschrieben hat sozusagen, ne? Auf der Ebene. Und ich habe dann eine Bibliothek, die mir das bietet, wo ich mich dann nur noch rein hängen muss. Dann habe ich da eine Funktion supervision. Also nein, ich habe keine Funktion, ich habe eine ganze Applikation. Ich starte das und der macht dann so einen Supervisor auf, also einen Prozess und dann kann ich sagen “Linkt mal die und die dran und nimm die Strategie mit der Konfiguration.” Und das ist dann halt ein Dreizeiler und dann hängt der halt unter so einem Supervision Tree, mein Prozess.

Till Schulte-Coerne: Was ist jetzt eine Applikation im Unterschied?

Christoph Iserlohn: Ja das ist, genau. Das ist keine Applikation, wie man das jetzt so im normalen Sinne kennt, sondern …

Till Schulte-Coerne: Nichts bei Erlang ist so, wie man es im normalen Sinne kennt.

Christoph Iserlohn: Ja, sagen wir mal so, es ist halt älter als das und hat seine Sprechweise und Notation halt so beibehalten. Also eine Applikation ist sowas, das sind halt mehrere Prozesse, die irgendeine Aufgabe erfüllen. Also Applikationen finden im Erlang-System statt und ich kann da diverse Applikationen starten. Zum Beispiel das Logging-System ist eine Applikation, ne? Dann sage ich “Ok, Logging wird gestartet.” Die Applikation, woanders würde man sagen Logging-Framework, hat dann einen Prozess, wo ich dann irgendwo die Nachrichten hinschicke, die geloggt werden sollen. Und der hat dann Worker-Prozesse, die z. B. das in den Syslog schreiben oder auf die Platte schreiben usw. Und der hat auch seine eigenen Supervisor da drin. Also eine App sind halt eine bestimmte Anzahl von Prozessen, die eine Funktion erfüllen. Und die kann ich dann halt, die sind dann halt sozusagen, naja nicht packetiert, aber in einem – wie soll man das sagen? – gebündelt. Und dann habe ich irgendwo den Einsprungsort und sage “Starte mal das Logging-Sub-System.” Oder “Starte mal den internen HTTP-Server.”, das ist auch eine App. Oder meinen Client. Oder mein SSL.

Till Schulte-Coerne: Also quasi die gröbste Gruppierungseigenschaft im Erlang-Universum.

Christoph Iserlohn: Genau, das ist die gröbste Gruppierung in so einem Erlang-System. Also man hat Prozesse und die bündel ich dann irgendwie zusammen und habe dann eine Applikation. Also, das sind in anderen, also im Java-Bereich wäre das so ein JavaEE-Container, wäre das so eine Art Service, der bereitgestellt wird vom Container. Oder den ich in einen Container stecken kann.

Till Schulte-Coerne: Ja.

Christoph Iserlohn: Mit dem kann ich halt kommunizieren über eine definierte Schnittstelle. Die nehmen mir ja dann auch dieses Message-Passing ab. Also eine App, mit der rede ich jetzt nicht, indem ich der oft Nachrichten schicke, sondern die bietet mir eine Client-Bibliothek, und sag ich dann “Log das mal” und der wandelt das dann intern in so eine Prozess-Nachricht um. Und das brauche ich alles nicht selbst zu machen.

Till Schulte-Coerne: Das ist eh guter Ziel, oder? Dass man das dann so macht.

Christoph Iserlohn: Genau.

Till Schulte-Coerne: Also wenn man sozusagen Prozesse der Außenwelt bereitstellt, dann dokumentiert man nicht – um nochmal zwei Schritte zurück zu gehen quasi – definiert man nicht die Nachrichten, auf die man reagiert, sondern man definiert eine programmatische API, die dann diese Nachrichten absendet, ne?

Christoph Iserlohn: Wir können das mal am Beispiel nehmen. Es gibt in diesem OTP auch das sogenannte gen_server, gibt es da. Die sind halt um eine Art Server-Client-Kommunikation nachzustellen. Wie würde ich das jetzt machen, wenn das nicht hätte? Dann würde ich einen Prozess starten, der wartet halt auf Nachrichten und bearbeitet die und schickt demjenigen, der die Anfrage geschickt hat, eine Antwort, ne? Ganz einfach. Jetzt muss ich aber immer da drum sehen, dass ich diese Schleife mache, dass der immer die Nachrichten beantwortet, dass der mit Timeout arbeitet, wie der das zurück schickt und so. Und das ist halt sehr repetitiv bei allen Arten von Servern, die ich in meinem System habe.

Till Schulte-Coerne: Ja.

Christoph Iserlohn: So. Und in OTP gibt es einen gen_server – generic server halt – und der nimmt einem das alles ab. So. Sondern der hat, für den definiere ich sozusagen nur noch ein paar Callbacks, sage “Wie passiert eigentlich die fachliche Verarbeitung.” Das ganze Umwandeln in Nachrichten und mit Timeouts arbeiten und so, das ist dann rein konfigurativ. Da sage ich “Starte mir mal einen gen_server.”, “Ok, Nachricht darf maximal …” oder “Nachricht X oder Anfrage X darf nur maximal fünf Sekunden Verarbeitungszeit brauchen.” Dann kriegst du einen Timeout. Und das schicke ich ihm per Konfiguration rein und dann startet der den und macht das. Und ich – nach außen definiere ich dann selbst eine API, die ich dann einfach über Funktionen aufrufe. Das ist dann von mir gekapselt und ich brauche mich um diese low-level Kommunikation nicht zu kümmern. Und diese Konstrukte greifen …

Till Schulte-Coerne: Wie viele von diesen, von solchen Konstrukte gibt es denn eigentlich so im OTP-Universum?

Christoph Iserlohn: Es gibt halt, es sind gar nicht so viele. Es gibt den Server, es gibt State Machines, es gibt Event Handler und es gibt die Supervison Trees. Das sind eigentlich die großen, die man hat, an Pattern, die durch irgendwelchen Code, der mitgeliefert wird, einem weggekapselt werden. Nicht weggekapselt, sondern die Abstrahierung leichter machen, dass ich für den Anwender wegkapseln kann. Die gibt es, das sind die großen Dinger. OTP umfasst noch mehr, also es gibt halt Support-Bibliotheken, wenn ich meine eigenen Prozesse da rein hängen will. Es gibt Tools, in denen ich so ein ganzes Erlang-System zusammenbaue, ein Bundle mache, dass ich dann irgendwo hinschieben kann und entpacken kann. Es hilft mir beim Thema Hot-Code-Reloading. Da kommen wir später nochmal zu. Erlang kann halt Code zur Laufzeit austauschen. Und wenn ich jetzt so einen gen_server habe, habe ich dann auch einen Callback für “Was soll den passieren, wenn sich der Code ändert.” Dann kann ich halt irgendwelche Dinge noch machen, um z. B. meine Daten zu transformieren, weil die Codeänderung jetzt ein neues Datenmodell z. B. mitbringt und so. Und das wird alles, das muss ich halt nicht immer neu machen, das ist da halt alles schön vorbereitet. Und das heißt, ich kann wieder auf einer normalen Ebene arbeiten, dass ich sage “Funktionen rufen Funktionen auf.” Und ich brauche diese Kommunikation, die natürlich dann auch bei vielen Prozessen dann sehr komplex wird, muss ich dann nicht mehr auf der Low-level Ebene immer neu nachbauen.

Till Schulte-Coerne: Also, wir haben, um das jetzt nochmal zusammenzufassen. Wir haben die Sprache.

Christoph Iserlohn: Ja.

Till Schulte-Coerne: Die man erstmal lernen muss. Wir haben das Aktoren-basierte Arbeiten mit Message Passing und ähnlichem.

Christoph Iserlohn: Genau.

Till Schulte-Coerne: Wir haben dann OTP als sozusagen den dritten Meilenstein, den man dann irgendwann mal verstehen muss.

Christoph Iserlohn: Ja, das ist halt so der Glue, der alles zusammenhält davon. Mit dem ich richtig Systeme designen kann, ne? Das ist halt, bietet mir, kapselt alles low-level für mich weg.

Till Schulte-Coerne: Macht denn Erlang ohne OTP Sinn?

Christoph Iserlohn: Eigentlich relativ wenig, ne? Also man kann das so machen, aber man bereitet sich damit nur mehr Arbeit und mehr Schmerz und lernt das, was die, die OTP zum größten Teil geschrieben haben, macht man dann irgendwann selbst. Also ich würde es nicht empfehlen, das zu tun, ohne OTP.

Till Schulte-Coerne: Ja.

Christoph Iserlohn: Es wird ja auch mit OTP ausgeliefert. Es ist immer dabei. Also es ist eigentlich fester Bestandteil. Früher war das mal nicht so. Es gab Erlang und OTP war so ein Produkt, was – das war so in der proprietären Phase – was man kaufen konnte.

Till Schulte-Coerne: Ah, ok.

Christoph Iserlohn: Aber jetzt ist es halt eins und das nicht zu benutzen, ist eigentlich fahrlässig.

Till Schulte-Coerne: Ok. Wie steht es denn mit Erlang im Vergleich zu den aktuell vielleicht gängigen Programmiersprachen eigentlich so? Also Java, Scala, Akka nimmt ja für sich auch Anspruch, Aktoren zu beherrschen. Und Go hattest du ja schon angesprochen.

Christoph Iserlohn: Ja, also aktuell versuchen die Programmiersprachen auch so ein Modell zu integrieren, also das Aktorenmodell. In der ein oder anderen Form. Das klappt mehr oder minder gut. Ich glaube, dass es eher nicht so gut klappt und das liegt daran, dass die JavaVM halt nicht alles so her gibt. Die ErlangVM macht da Dinge anders, und zwar besser für dieses Modell. Also ich habe z. B. eine Garbage Collection auf Prozessebene, die dann einen Prozess anhält und das ganz schnell geht und nicht eine globale Garbage Collection, die den gesamten Heap dann macht, ne? Dann habe ich, das Scheduling kann die ErlangVM preemptive machen, das heißt, die kann Prozesse, die jetzt in irgendeine Endlosschleife geraten sind und dann wild drehen und 100% CPU verbrauchen, auch anhalten dabei. Das kann die JVM auch nicht, das kann auch Go nicht – die mit ihren Go-Routinen – machen. Da kann ich halt eine Go-Routine in eine Endlosschleife versetzen und die powert dann halt durch. Das kann Erlang. Und das – das hatte ich ja am Anfang angesprochen – es gibt zwar mehrere Implementierungen, aber ein System, das dann wirklich tolerant gegenüber Fehlern ist und durchgängig läuft, kriege ich mit einer JVM, meines Erachtens, auch zur Zeit nicht mit Go, kann ich das nicht programmieren. Da ist die VM einfach nicht für ausgelegt.

Till Schulte-Coerne: Ja, also wenn man wirklich 100% Laufzeit, das geht ja eh nicht. Aber sehr viel muss man ja auch nachladen können.

Christoph Iserlohn: Ja, das ist auch ein, was die JVM kann. Sie kann halt Code zur Laufzeit ersetzen. Das geht auch durch das Erlangmodell ganz gut. Es gibt wenig State, ich muss nicht gucken, dass sich Objekte ihren State behalten und dann auf einmal eine neue Klasse sind, sondern das ist einfach da. Die Funktionen werden ersetzt und beim Funktionsaufruf wird halt der gesamte State, der nötig ist, mit übergeben. Das kann die JVM auch nicht. Es gibt da, man kennt das ja, in Java auch so Code-Reloading in so einem Tomcat oder JEE-Server. Aber dann hat man immer das Problem, dass es irgendwo Speicherlecks gibt und nach dem dritten Mal, nachdem man das gemacht hat, dann muss man den halt neu starten. Das funktioniert halt nicht. Und da werden halt die Interna auch so anders abgebildet, dass das auch nicht gehen wird, ne? Also von daher. Und jetzt Go, hatten wir angesprochen, was ja ähnlich Primitive hat, kann das ja gar nicht gehen. Das hat ja so ein statisches Binary wird erzeugt, dann nativer Assembler Code. Da ist dann auch vorbei.

Till Schulte-Coerne: Das ist letztenendes eine sehr einzigartige Programmiersprache und VM und nicht unbedingt – also die Entscheidung, ob ich jetzt Java nehme oder Scala oder Erlang sollte eigentlich relativ klar sein, unter gewissen Voraussetzungen, dass man dann auch Erlang nimmt, oder? Auch wenn es natürlich anders gehandhabt wird.

Christoph Iserlohn: Ja.

Till Schulte-Coerne: Weil eben bestimmte Eigenschaften ansonsten gar nicht garantiert werden können.

Christoph Iserlohn: Ja, also das Problem ist halt, dass ich mit anderen, mit Akka und Konsorten halt sozusagen die Fassade gleich habe, aber das Interne, der Innenraum ist halt nicht gleich und damit kann ich solche Anforderungen, dass es halt möglichst lange läuft und während des Betriebs gewartet werden kann, kann ich damit halt nicht so erfüllen. Da habe ich vielleicht andere Vorteile, die diese Programmiermodelle sonst noch mitbringen, so von der Lesbarkeit, von der Strukturierung. Aber das eigentliche, wenn man die ursprüngliche Problemdomäne sich anguckt, das kriege ich damit dann halt nicht hin. Langlaufende Systeme.

Till Schulte-Coerne: Wie sieht es denn mit der Lesbarkeit aus? Also es gibt ja jetzt gerade den Hype, die Hype-Sprache Elixir, wie auch immer man die ausspricht.

Christoph Iserlohn: Ich habe verschiedene Aussprachen gehört. Elixir habe ich es jetzt in diversen Podcasts gehört, bleiben wir mal dabei. [Anm.: die verwendete Aussprache der Gesprächspartner lässt sich hier natürlich nicht vermitteln.] Ja, Elixir wird ziemlich gehypt. Das setzt halt auf der ErlangVM auf, benutzt auch OTP und orientiert sich aber an der Syntax an Ruby.

Till Schulte-Coerne: Ach?

Christoph Iserlohn: Und soll halt andere Programmierer anlocken, die halt mit der prologartigen Syntax von Erlang nicht so klar kommen. Und wenn man Ruby kennt, sieht es auch ganz schön aus. Über Ruby Syntax kann man sich auch streiten, ob die schön ist oder nicht. Aber das hat im Moment eine hohe Traktion. Da ist jetzt auch die Version 1.0 vor kurzem rausgekommen. Und mal sehen, ob das noch ein Hype bleibt oder nicht. Aber auf jeden Fall kann man dazu festhalten, dass zwei der ursprünglichen Erlang-Entwickler sich sehr positiv darüber geäußert haben. Also das auch gut finden. Also, es stößt jetzt nicht auf Ablehnung in der Erlang-Community.

Till Schulte-Coerne: Wie ist es denn kompatibel? Also, kann ich die Sachen mischen? Ist es nur eine andere Syntaxvariante, so ähnlich wie Coffeescript zu Javascript? Oder bekomme ich dadurch auch neue Programmier-Paradigmen rein und ähnliches?

Christoph Iserlohn: Ja, ja und nein. Also es ist kompatibel, es kompiliert halt zu Erlang-Bytecode und den kann ich auch nutzen. Und am Ende stehen da irgendwelche Funktionen, die ich aufrufe. Das geht. Das Problem ist halt, auf der Sourcecode-Ebene gibt es natürlich Schwierigkeiten. Und da bringt Elixir auf der Sourcecode-Ebene auch andere Paradigmen rein. Also es hat z. B. Makros, die ich da benutzen kann. Es ist auch nicht mehr, dass die Variable nicht mehr so zuweisbar, einmal nur zuweisbar sind, sondern es benutzt halt eine Standardsyntax und auch die Paradigmen davon. Und dann wird es halt manchmal schwierig, dann hat man einen Missmatch zwischen diesen Paradigmen auf der Ebene. Ob das dann noch passt, weiß ich nicht. Aber grundsätzlich ist das interoperabel. Also ich habe Erlang-Bytecode am Ende und den kann ich nutzen. Schwierig ist es halt, weil die meiste Integration in der Erlang-Welt passiert nicht, indem ich Bytecode irgendwo her lade, also kein Jar-artiges Artefakt. Sondern normalerweise binde ich den gesamten Source immer ein. Also Pakete werden normalerweise in Erlang als Source eingebunden. Dann habe ich irgendwie irgendwo noch den Lib-Ordner und da sind die Sourcen von allen drin. Da kann ich das halt natürlich nicht mixen.

Till Schulte-Coerne: Ja, was würdest du denn sagen, wenn man sich heute mit dem Thema beschäftigen will. Auf die Idee kommt, jetzt Erlang zu machen bzw. mal in diese Welt der Hochverfügbarkeit und massiven Parallelität einzusteigen, würdest du dann eher Erlang empfehlen oder eben Elixir?

Christoph Iserlohn: Es kommt drauf an, welche Startvoraussetzung ich habe. Wenn ich Ruby kann und ich will mich damit beschäftigen, würde ich Elixir empfehlen. Da habe ich wenig Schwierigkeiten mit der Syntax, kann mir aber das ganze OTP-Zeug und Prozesse und Kommunikation alles mal angucken. Und schauen, passt das denn zu dem Problem, was ich da habe? Will ich das gerne so machen? Man kommt aber dann nicht drumherum, auch wirklich Erlang zu machen. Dafür ist halt das Ökosystem noch viel größer. Und es ist auch ausgereifter an der Stelle. Wenn ich Ruby auch noch gar nicht kenne, dann würde ich vielleicht auch direkt noch mit Erlang anfangen. Weil Erlang ist etabliert, es ist zwar eine Nische, aber daran – also was heißt Nische – in der Nische etabliert. Es gibt eine feste Community, es gibt eine Firma, die dahinter steht. Das wird jetzt nicht so leicht verschwinden. Elixir kann man noch nicht beurteilen, es ist ein Hype. Also das heißt, wenn ich das noch nicht kann, würde ich eher auf Erlang erstmal setzen und mir vielleicht später Elixir aneignen.

Till Schulte-Coerne: Und wie mache ich das dann am besten? Hast du da irgendwelche Empfehlungen?

Christoph Iserlohn: Ja, es gibt halt, die Erlangdokumentation hat ein Tutorial drin. Und wenn ich, es gibt ein paar gute Bücher von Joe Armstrong, da ist einer der ursprünglichen Erfinder. Es gibt von O’Reily ein gutes Buch, können wir in die Shownotes die Titel verlinken. Aber wenn ich jetzt mal so unverbindlich anfange, würde ich eine Web-Ressource nehmen. Das ist “Learn you some Erlang for great good”. Das hat ein sehr, in der Erlang-Community sehr Bekannter geschrieben und es ist eine sehr gute Ressource, wo er vor allem auch auf die Paradigmen eingeht. Also es ist nicht nur Syntax, sondern er erklärt auch, warum macht man denn jetzt so einen Server genau so, wie es ja in OTP gemacht wird. Also der Hintergrund wird gut erklärt. Es ist halt kostenlos im Web einsehbar. Wenn ich es woanders lesen will, kann ich es mir auch als Buch bestellen und das ist glaube ich der beste Einsteig. Die Nummer eins Empfehlung, um da mal einfach reinzukommen.

Till Schulte-Coerne: Nehmen wir entsprechend in die Shownotes auf.

Christoph Iserlohn: Werden wir da aufnehmen. Und der Start ist relativ einfach. Erlang hat halt eine interaktive Shell. Wenn ich es installiert habe, kann ich auch direkt loslegen. Also es gibt halt nicht groß “Ich muss was kompilieren und hier und da” und mir erstmal meine ganzen Tools laden, mit denen ich mein System zusammen baue, sondern interaktive Shell auf und ich kann direkt loslegen.

Till Schulte-Coerne: Wofür würdest du es denn überhaupt empfehlen? Also was ist der klassische Einsatzbereich von Erlang? Sowas wie Whatsapp ist relativ offensichtlich.

Christoph Iserlohn: Whatsapp ist …

Till Schulte-Coerne: Aber das Bauen die wenigsten Leute.

Christoph Iserlohn: Bauen die wenigsten Leute.

Till Schulte-Coerne: NoSQL-Datenbanken baut man auch klassischerweise mit Erlang, glaube ich.

Christoph Iserlohn: Hat man auch gemacht, ja. Obwohl die oft ein Erlang und C Mix sind.

Till Schulte-Coerne: Aber das …

Christoph Iserlohn: Wird halt für den high-performance Teil genommen. Also ich würde es nicht nehmen, wenn ich irgendwas mit high-performance Numerik habe. Also Number crunching. Da ist halt eine höhere Programmiersprache, ist da nicht schlecht. Aber wenn ich immer so eine Art irgendwie Command-Control-Server haben will, der irgendwas steuert, dann ist es eine sehr gute Anwendung dabei. Also zum Beispiel könnte ich mir vorstellen …

Till Schulte-Coerne: Dann müsste es doch eigentlich in der Heimautomatisierung, IoT-Kontext müsste das doch einschlagen wie eine Bombe. Passiert gerade irgendwie nicht, ne?

Christoph Iserlohn: Ja. Bis jetzt weiß ich das nicht, aber da machen wir bestimmt mal einen Podcast mit unserem anderen Kollegen, der da mal was gemacht hat. Das erlything. Der kann da vielleicht besser was drüber erzählen. Ich kenne mich in der Heimautomatisierung überhaupt nicht aus. Weder in den anderen Sprachen, noch in Erlang.

Till Schulte-Coerne: Aber es klingt für mich irgendwie immer so, als wäre das ein ziemlich offensichtlicher Match eigentlich.

Christoph Iserlohn: Könnte man, kann ich mir gut vorstellen. Also.

Till Schulte-Coerne: Da will man ja auch nicht mehr, dass nachts das Klolicht nicht mehr angeht.

Christoph Iserlohn: Ja.

Till Schulte-Coerne: Gerade der Server abgestürtzt ist.

Christoph Iserlohn: Ja.

Till Schulte-Coerne: Ja, hast du noch abschließende Worte, abschließende Bemerkungen zum Thema? Oder?

Christoph Iserlohn: Ja, ich kann jedem nur empfehlen, das mal auszuprobieren. Und auch wenn man es nicht einsetzt, unbedingt produktiv, kann man auf jeden Fall viel darüber lernen, wie man halt seine Systeme gestalten kann. Also wenn man langlaufende Systeme hat, lernt man davon, nur von den Paradigmen kann man sich was abschauen.

Till Schulte-Coerne: Super. Dann vielen Dank für das nette Gespräch und ja, bis zum nächsten Mal, zur nächsten Erlang-Folge hoffentlich.

Christoph Iserlohn: Alles klar.