Podcast

Clojure

Ein pragmatisches Lisp auf der JVM

Stefan Tilkov unterhält sich mit Philipp Schirmacher über Clojure, ein pragmatisches und praxistaugliches Lisp für die Java Virtual Machine.
Weitere Episoden anhören

Shownotes & Links

Transkript

Transkript ausklappen / einklappen

Philipp Schirmacher: Hallo Stefan. Ich arbeite jetzt seit eineinhalb Jahren ungefähr bei innoQ. Bin hier schwerpunktmäßig als Entwickler in Java-Projekten unterwegs und da meistens auf der Server-Seite. Ja, relativ viele Sachen im Web-Umfeld, wie man das heute so macht.

Stefan Tilkov: Wenn du eigentlich im Java-Umfeld tätig bist, warum reden wir über Clojure, was verbindet dich mit Clojure?

Philipp Schirmacher: Ich hatte so vor knapp drei Jahren bei meinem damaligen Arbeitgeber die Möglichkeit, eine Studie durchzuführen, wo ich Clojure und Skala auf ihre Tauglichkeit für die Entwicklung von Web-Applikationen – speziell auch im Enterprise-Umfeld – untersucht hab. Und da habe ich mich eben in die beiden Sprachen eingearbeitet, in die verschiedenen Bibliotheken, die man da in dem Zusammenhang benutzen kann und fand das ganz gut und speziell Clojure hat sich seitdem so ein bisschen zu meinem persönlichen Steckenpferd entwickelt.

Stefan Tilkov: Ok, auch wenn ich annehme, dass die meisten unserer Zuhörer so grob wissen, was sich hinter Clojure verbirgt, lass uns doch mal starten, indem du uns vielleicht kurz erklärst, was Clojure so ausmacht. Was ist das und was sind so die wichtigsten Feature, die man bei Clojure findet?

Philipp Schirmacher: Also was man bei Clojure meistens als erstes hört, wenn man irgendwie was davon erzählt bekommt oder irgendwie einen Artikel drüber ließt, ist eigentlich so diese ganze Concurrency-Multicore Geschichte. Clojure unterstützt ja auch Software Transactional Memory und ein paar Sachen in der Richtung. Das sind schöne Features, aber ich persönlich finde eigentlich und habe es auch schon von verschiedenen anderen Leuten gehört, die irgendwie mehr damit gemacht haben, dass man, wenn man damit arbeitet, eigentlich das gar nicht so in den Vordergrund drückt, sondern was ich eigentlich bei Clojure vor allem gut finde, ist diese funktionale Programmierung. Also das finde ich ist was, was Clojure stark von beispielsweise Java unterscheidet. Und was sehr gut ist so in der alltäglichen Arbeit, dass man eben seine Programme anders strukturiert. Also man hat nicht dieses objektorientierte, wo man Daten und Funktionen in Klassen zusammenfasst, sondern man hat grundsätzlich Daten und Funktionen voneinander getrennt. D.h., der Großteil des Programms besteht aus puren Funktionen, die Input-Argumente bekommen, aus dem Input einen Output erzeugen und eigentlich normalerweise keine Seiteneffekte haben. Genau, „pure Funktion“ heißt „Funktion ohne Seiteneffekte“. D.h., wenn man die mit dem gleichen Argument aufruft, mit den gleichen Werten aufruft, liefern die auch immer das gleiche zurück. Das finde ich erstmal sehr angenehm, weil das dazu führt, dass man Applikationen leichter versteht und leichter testen kann.

Stefan Tilkov: Passt ja auch wieder zu Concurrency.

Philipp Schirmacher: Genau, das ist auch wieder ein großer Vorteil mit Concurrency, weil eben Concurrency-Probleme gerade dann oft auftreten, wenn sich irgendwie verschiedene Funktionen oder Methoden einen Zustand teilen und man durch den Aufruf der Methode auch einen Seiteneffekt erzeugt, durch irgendwie einen geteilten Zustand, der verändert wird. Und durch das Clojure-Programmiermodell wird das eben schon im Ansatz wesentlich seltener gemacht, als bei Java. Genau, also an den Stellen, wo man es machen muss, benutzt man das Software Transactional Memory, aber man macht es grundsätzlich einfach viel weniger. D.h., einfach das Programmiermodell an sich ist ganz gut für Concurrency geeignet.

Stefan Tilkov: Jetzt hast du es aber zwei mal erwähnt, jetzt müssen wir auch kurz erklären, was Software Transactional Memory denn eigentlich ist.

Philipp Schirmacher: Also im Prinzip ist das Software Transactional Memory in Clojure so umgesetzt, dass man im Prinzip eine Art Pointer hat. Also es gibt verschiedene Typen von Pointer – ein Pointer zeigt auf einen Wert. Und diese Werte sind erstmal unveränderlich. Also z.B. ein Vektor von Benutzern in der Applikation oder ähnliches. Oder Produkten, also irgendwelche Applikationsdaten. Und der Zustand lässt sich in Anführungszeichen ändern, indem man eben den Pointer auf einen neuen Wert zeigen lässt. Und so kann man eben Veränderungen im Zeitablauf in der Applikation darstellen und der Vorteil besteht eben darin, dass dieses Umsetzen des Pointers an sich eine threadsichere Semantik hat. Also es ist eben durch Clojure sicher gestellt, dass alle Threads, die z.B. so einen Pointer dereferenzieren, einen konsistenten Wert sehen. Und genau es gibt eben verschiedene Arten von Pointern, die sich eben z.B. darin unterscheiden, ob das Umsetzen des Pointers auf einen neuen Wert synchron oder asynchron geschieht oder ob das koordiniert geschieht. Also man kann z.B. in so einer Art Transaktionsklammer mehrere Pointer gleichzeitig umsetzen, an Stellen, wo man das braucht. Da gibt es also verschiedene Arten von Pointern, die aber alle threadsicher sind und genau, wenn man in einer Applikation irgendwo einen Zustand halten möchte, benutzt man üblicherweise sowas an wenigen isolierten Stellen. Aber der Rerst der Applikation ist eigentlich zustandslos.

Stefan Tilkov: Ja, das hat auch noch andere schöne Seiteneffekte, wie z.B. dass man keine Concurrent-Modification-Exception bekommt, wenn man über irgendwas drüber iteriert, weil sich das, worüber man iteriert, eben nicht ändern kann. Kann nicht passieren.

Philipp Schirmacher: Ja genau.

Stefan Tilkov: Ok, also wenn Concurrency nicht das ist, was wir in den Vordergrund stellen – auch wenn es ja ein tolles Feature ist – was sind denn dann die Dinge, die wir in den Vordergrund stellen würden? Du hast schon gesagt, „funktional“ ist ein wesentlicher Aspekt. Was gibt es noch zu Clojure zu sagen?

Philipp Schirmacher: Ja ein weiterer wesentlicher Punkt bei Clojure ist sicherlich, dass es dynamisch getypt ist.

Stefan Tilkov: Teufelszeug.

Philipp Schirmacher: Ja genau, Teufelszeug. Es ist immer ein bisschen Geschmackssache, ob man das jetzt mag oder nicht. Wo ich es persönlich einfach sehr gut finde ist beim arbeiten mit generischen Datenstrukturen – kommen wir vielleicht auch gleich nochmal drauf – da finde ich dynamische Typisierung auf jeden Fall einen Vorteil. Ja, ob man es mag oder nicht, es ist auf jeden Fall ein wesentliches Merkmal der Sprache. Was ich auch einfach sehr gut finde ist – das ist gar nicht mal wirklich ein Sprachfeature als solches, sondern einfach ein Tool, mit dem man in Clojure üblicherweise arbeitet – die sogenannte REPL. Steht für Read-Eval-Print-Loop und ist eine wichtige Voraussetzung dafür, dass man in Clojure eben das sogenannte „Interactive Development“ machen kann. Funktioniert einfach so, dass man während der Entwicklung immer eine laufende Applikation eben hat und über eine Kommandozeile – eben diese REPL – permanent eigentlich mit der laufenden Applikation interagiert und dadurch eigentlich so einen sehr explorativen Ansatz hat und sehr gute Möglichkeiten hat, dadurch eben Bibliotheken kennen zu lernen, Funktionen, die man schreibt, gleich auszuprobieren und ja einfach so ein bisschen rum zu spielen und hat ganz gut immer so ein Gefühl dafür, was die Applikation, die man so entwickelt, eigentlich wirklich macht. Ja, so eine REPL finde ich persönlich auch sehr wichtig bei Clojure und ich merke immer so, wenn man sich in eine neue Sprache einarbeitet, die sowas vielleicht nicht hat, dann fällt immer erstmal auf, wie hilfreich sowas eigentlich ist, weil gerade das Kennenlernen von neuen Bibliotheken usw. umso einfacher ist, je schneller man die eben ausprobieren kann, je schneller man Feedback bekommt.

Stefan Tilkov: Und es passt auch wieder ganz gut zu den seiteneffektfreien, puren Funktionen. Die eignen sich natürlich wunderbar, um sie mal eben auszuprobieren, weil man eben keinen globalen Zustand damit verändert.

Philipp Schirmacher: Genau, es ist nicht so, dass man für eine Applikation erst einen sehr großen Kontext aufbauen muss, bevor man überhaupt mal irgendwas tun kann, sondern dadurch, dass eine Applikation einfach nur aus vielen kleinen eigentlich isolierten Funktionen besteht, hat man eben auch viele Punkte, wo man einfach mal reinspringen kann, um eben ein bestimmtes Verhalten einfach auszuprobieren.

Stefan Tilkov: Suggestiv-Frage: Heißt das dann, dass man da mit so einem Testing-Zeug nicht groß was am Hut hat, sondern einfach ein bisschen vor sich hin hackt oder gibt’s da auch Test-Support?

Philipp Schirmacher: Äh, da gibt es Test-Support, also tatsächlich ist auch in Clojure direkt schon ein – sowas JUnit-artiges – Framework eingebaut. Man kann sogar Funktionen direkt schon mit Test-Fällen als Metadaten sozusagen versehen. Das ist auch in manchen Fällen ein ganz cooles Feature. Ja also doch, man sollte durchaus noch testen. Die REPL verführt manchmal dazu, dass man denkt: „Och, funktioniert ja, habe ich ja schon ausprobiert.“ Die Tests zu schreiben ist trotzdem nicht verkehrt.

Stefan Tilkov: Ok. Du hast grad kurz schon mal diese generische Daten- Strukturen angesprochen. Ich glaube das ist ein ganz wichtiger Punkt, grad wenn wir das mit anderen Sprachen vergleichen. Also wenn man sich jetzt vorstellt, man hat diese Funktionen in Namespaces – hast du grad schon kurz erklärt – , die auf irgendwelchen Daten-Strukturen, generischen Daten- Strukturen arbeiten, dann klingt das so, als würde man anders vorgehen, als in einer statisch getypten Programmiersprache. Das musst du vielleicht ein bisschen erläutern, weil man sich das so abstrakt nur relativ schwer vorstellen kann.

Philipp Schirmacher: Genau, also generische Daten-Strukturen bedeutet eben im Wesentlichen Dinge, wie Listen, Vektoren, Maps, Sets usw. Angenommen man schreibt eine Applikation, wo man eben irgendwelche Bestellungen oder Produktdaten verwalten möchte, geht man ja üblicherweise in einer Sprache wie Java so vor, dass man eben entsprechenden Klassen erstellt. Also man hat eine Klasse Product oder was auch immer man modellieren möchte, die die entsprechenden Daten hält und Methoden anbietet, um auf dieses Daten zu arbeiten. Und in Clojure ist es im Gegensatz so, dass man eben diese generischen Datentypen benutzt, um die Daten zu halten. Also anstatt einer Java-Bean, hat man eben eine Map, mit einer Person z.B. einem Key „Vorname“ und einem Key „Nachname“ und einem Key „Alter“, wo man eben die entsprechenden Werte drunter ablegt. Genau und anstatt eine Klasse zu haben, die die Methoden anbietet, um mit diesen Personen dann zu arbeiten, hat man einfach eben in diesen Namespaces – also in den Packages sozusagen in Clojure – Funktionen, die so eine Map entgegen nehmen und dann damit bestimmte Berechnungen durchführen oder was auch immer.

Stefan Tilkov: Und das hat welche Vorteile? Was ist daran gut, das so zu machen?

Philipp Schirmacher: Also was speziell in Clojure daran sehr schön ist, ist dass man schon eine sehr mächtige Bibliothek zur Verfügung hat, um mit diesen Collection usw. zu arbeiten. Also man hat z.B. Funktionen, wie map und filter, reduce, fold, wie man sie auch aus verschiedenen anderen Sprachen oder z.B. auch in Java aus der Guava-Bibliothek kennt. Ja, viele von diesen Funktionen setzen eben auch voraus, dass man Higher-Order- Function hat, also dass Funktionen selber auch Werte sind, die man einfach durch die Gegend reichen kann, so wie Instanzen anonymer, innerer Klassen in Java. Und durch diese sehr umfangreiche Bibliothek, die Clojure eben schon mitbringt, wird einem viel Arbeit abgenommen, sodass man selber tatsächlich relativ wenig Code nur schreiben muss – in vielen Fällen – um eben diese Daten zu verarbeiten.

Stefan Tilkov: Jetzt könnte ich das aber auch genauso gut in Java machen, also es hindert mich ja niemand daran in Java eine java.util.Hashmap zu benutzen, um darin irgendwelche Sachen unterzubringen und trotzdem tue ich das ja nicht, weil mich jeder Java-Programmierer erschlagen würde, wenn er solchen Code von mir übernehmen müsste. Warum ist das jetzt in Java ein schlechter Weg und in Clojure ein guter Weg? Warum, wie hängt das zusammen?

Philipp Schirmacher: Also in Java ist es, was man ja in Java dann z.B. tun könnte ist, man würde einfach nur Klassen haben mit public static Methoden, die irgendwie Hashmaps entgegen nehmen und Hashmaps zurückgeben. Genau, das ist – eine Schwierigkeit dabei wäre z.B. das Typsystem als solches, was einem in dem Fall tatsächlich nicht richtig helfen kann. Also angenommen ich möchte z.B. Personendaten verarbeiten, muss ich mir überlegen, was für einen Typ genau soll diese Map jetzt haben, die diese Daten hält. Es wäre dann wahrscheinlich irgendwie sowas, von String auf Object. D.h., immer wenn ich auf den Namen oder – genau, weil ich eben für die Person den Namen und das Alter speichern möchte, also ein mal eine Zahl, ein mal einen String. Gemeinsame Oberklasse ist nur noch Object. D.h., immer wenn ich auf irgendwelche Daten zugreifen möchte, muss ich erstmal casten. Damit umgehe ich erstmal das Typsystem. Also es ist sowieso dann nicht mehr typsicher, es hilft mir dann erstmal nichts mehr. Und es ist darüber hinaus auch noch einfach sehr unkomfortable. Also man kann eben nicht einfach auf die Daten zugreifen, sondern man muss immer noch casten. Es ist einfach schonmal sehr unhandlich, in der Arbeitsweise. Auch das gesamte Tooling im Java-Umfeld ist einfach nicht dafür gemacht, sondern speziell Hibernate und ähnliche Frameworks setzen ja auch sehr stark darauf, dass man eben mit Java-Beans arbeitet. Java-Beans sind ja sozusagen der Versuch in der Java-Welt einen generischen Daten-Zugriff zu ermöglichen, über Reflection eben. Das ist natürlich auch nicht typsicher, aber macht ja nichts. Und in dem Moment, in dem man anfängt mit Hashmaps z.B. zu arbeiten, funktionieren auch viele Frameworks einfach nicht mehr so, weil die nicht dafür gemacht sind. Ein weiteres Problem ist auch in Java, es ist eben nicht wirklich möglich, polymorphe Funktionen zu schreiben oder polymorphe Methoden zu schreiben, weil Polymorphie in Java eben immer nur über den Typ funktioniert. Also man kann nur über den Typ dispatchen, d.h., man muss, wenn man polymorphe Funktionen schreiben möchte auch selber neue Typen erstellen.

Stefan Tilkov: Das muss man in Clojure nicht.

Philipp Schirmacher: Genau, in Clojure funktioniert Polymorphie anders. Dadurch ist es da kein Problem. Also in Java müsste ich ja – was wäre denn ein Beispiel – ich habe irgendwie verschiedene Flächen und möchte deren Größe berechnen. Ich hab, keine Ahnung, Dreiecke und Quadrate. Da würde ich irgendwie die Methode „Größe berechnen“ in ein Interface schreiben und würde eine Klasse für Quadrate und eine für Dreiecke erstellen, die beide das Interface implementieren und in beiden Klassen eben die entsprechende Methode implementieren. In Clojure geht man stattdessen so vor, dass man eben die Daten – z.B. von Dreieck oder vom Quadrat oder wie auch immer – in einer Hashmap hält und eine Funktion schreibt „Größe berechnen“, die erstmal nur so eine Art Interface ist. Und von dieser Funktion kann man dann verschiedene Implementierungen bereit stellen, für verschiedene Typen, die man hat. Aber man hat grundsätzlich dieses Verhalten und die Daten als solche voneinander getrennt. Was auch verschiedene Vorteile mit sich bringt, z.B. ist es in Java sehr schwierig zu bestehenden Typen neue Funktionalität hinzuzufügen. Also angenommen ich möchte jetzt von meinen Dreiecken und Quadraten, die ich da in meine Applikation habe, nicht nur die Fläche berechnen, sondern auch noch den Umfang, dann muss ich das bestehende Interface ändern und muss die bestehenden Klassen ändern, also die bestehenden Dateien, was sowieso auch nur möglich ist, wenn ich die Klassen ursprünglich mal selbst erstellt habe. Ansonsten muss ich mir mit dem Adapter-Pattern helfen oder muss drauf hoffen, dass die irgendwie eine „accepted visitor“-Methode haben oder was auch immer. Und dadurch, dass eben in Clojure das voneinander getrennt ist, kann ich einfach hingehen, kann eine neue Funktion erstellen und auch für diese Funktion wieder verschiedene Implementierungen für verschiedene Daten, die ich hab – also Dreiecke, Quadrate, usw. – zur Verfügung stellen. Also in Java ist es ja sehr leicht neue Typen hinzuzufügen, also ich kann einfach einen neuen Typ erstellen – einen Kreis – der auch mein Interface implementiert. In Clojure gibt’s die Möglichkeit natürlich auch, aber zusätzlich kann ich eben nicht nur zu bestehender Funktionalität neue Typen hinzufügen, sondern umgekehrt auch zu bestehenden Typen neue Funktionen. Ja auch das macht die Applikation dann letztlich einfacher, weil ich eben nicht überall Adapter benötige.

Stefan Tilkov: Weißt du, was wir noch überhaupt nicht erwähnt haben? Wir haben glaube ich noch überhaupt nicht erwähnt, dass Clojure ein LISP ist. Wir haben auch wenig dazu gesagt, wo das eigentlich läuft, auch interessant. Normalerweise würde man das immer am Anfang sagen. Jetzt haben wir es halt eben von hinten aufgezäumt.

Philipp Schirmacher: Ja aber wir haben ja schon so viel über Java gesprochen, dass klar ist, dass es irgendwo um die JVM geht.

Stefan Tilkov: Genau, also vielleicht kannst du dazu auch noch kurz was sagen, wie Clojure sich eigentlich in das Java-Ökosystem einbettet?

Philipp Schirmacher: Genau, also wie zu eben schon gesagt hast, Clojure ist ein LISP. Also die Syntax ist sehr anders, als bei Java. Aber letztendlich ist Clojure eigentlich nur ein andere Compiler um Bytecode zu erzeugen. Also man benutzt eben nicht Java als Sprache und den Java-Compiler, sondern eben eine andere Sprache – ein LISP – und den Clojure-Compiler. Aber am Ende hat man den gleichen Bytecode, was natürlich den Vorteil einfach mit sich bringt, dass ich so das ganze Java-Ökosystem, was es gibt – also Servlet-Container, Datenbank-Zugriff mit JDBC, Logging mit Log4J – die ganzen Sachen, die man üblicherweise aus Java gewohnt ist, sich aus Clojure erstmal genauso benutzen lassen.

Stefan Tilkov: Man kann z.B. – habe ich neulich mal wieder mit rumgespielt – mit der Clojure-REPL ganz prima Java-Code ausprobieren, weil die Interoperabilität so angenehm ist, dass man relativ leicht Instanzen von irgendwelchen Java-Klassen erzeugen und auf diesen irgendwelche Methoden aufrufen kann. Und das alles interaktiv auf der Clojure-REPL.

Philipp Schirmacher: Genau ja. Das geht sehr leicht. Dadurch, dass auch das Standard-Clojure-Buildtool, also Leiningen, direkt Maven-Repositories unterstützt, mache ich auch manchmal, genau einfach: Projekt anlegen, Maven-Dependency hinzufügen. Dann hat man direkt die Klassen auf dem Klassen-Pfad und kann direkt damit rumspielen, es ausprobieren.

Stefan Tilkov: Ein nettes LISP-Feature sind noch Makros. Wie sieht’s bei Clojure mit Makros aus?

Philipp Schirmacher: Ja, gibt’s auch. Genau, Makros sind ja sozusagen das LISP-Feature für Meta-Programmierung. Also grundsätzlich geht es darum, Code zu schreiben, der wiederum Code schreibt. Also eine Art Code-Generator. Wenn man z.B. auch einen macht, indem man einfach ein Programm schreibt, das mit XML-Dateien gefüttert wird und daraus Java Source-Code generiert. Das ist ein Code-Generator und irgendwie eine Form von Meta-Programmierung. Und mit Clojure Makros oder generell in LISP mit Makros ist sowas eben direkt in den Compilierungsprozess eingebettet. Es funktioniert so, dass das Compilieren bei Clojure im Prinzip in zwei Schritten vonstatten geht. Also in dem ersten Schritt wird der Source-Code, den man schreibt, geparset und im Speicher eben Datenstrukturen darauf aufgebaut, was ein sehr kleiner Schritt ist, da man ja schon direkt Datenstrukturen eben hinschreibt. Und diese Datenstrukturen werden dann in den Compiler gegeben und der Compiler erzeugt aus diesen Datenstrukturen eben Bytecode. Und durch diese Trennung und durch diese Tatsache, dass eben der Compiler nicht Text bekommt, sondern Listen, Vektoren – also einfach Datenstrukturen – hat man natürlich die Möglichkeit, da nochmal einzugreifen und diese Datenstrukturen zu transformieren. Also man kann an der Stelle eben weiteren Code hinzugenerieren oder das Programm verändern in mehr oder weniger beliebiger Art und Weise. Eine Möglichkeit wäre z.B., dass man ein Makro macht – das nennt sich Transaction –, das irgendwie ein bisschen Code kapselt und bei der Auswertung des Makros wird es eben expandiert, sodass der Code, der von dem Makro gekapselt wird, vielleicht im Kontext einer Datenbank-Transaktion ausgeführt wird. Also wenn alles gut geht, wird einfach die Transaktion commited, wenn eine Exception fliegt, wird sie zurückgerollt. Sowas z.B. könnte man machen.

Stefan Tilkov: Man kann sozusagen den Code, den man schreibt, praktisch einwickeln in etwas und dann wird der Code, den man da eingewickelt hat, praktisch als Parameter an die Code-Generierungsfunktion übergeben, die dann irgendwas drum herum machen kann und dann in der Mitte diesen Code eben einfügt. Sowas klingt sehr abstrakt, aber ist eine sehr naheliegende Art und Weise.

Philipp Schirmacher: Genau. Und es ist tatsächlich in Java auch nicht so leicht. Deswegen hat man da immer diese schönen @transactional-Annotationen.

Stefan Tilkov: Ok, gut. Vielleicht können wir das ganze mal ein bisschen vergleichen mit anderen Sprachen. Also ein paar haben wir ja schon gesagt, wir haben ein bisschen Java oder du hast schon etwas mit Java verglichen. Was gibt es sonst noch so? Java, Scala, Groovy – wo würden wir Clojure da positionieren im Vergleich zu diesen anderen Sprachen?

Philipp Schirmacher: Ja, also so ein wichtiges Kriterium ist immer das Typsystem – also statisch oder dynamisch getypt. Und das Programmiermodell, also eher funktional oder eher objektorientiert. Im Gegensatz zu Java, wie schon gesagt, ist Clojure eben funktional und nicht objektorientiert und es ist dynamisch getypt und nicht statisch getypt. Groovy bin ich jetzt kein Experte für, aber ist ja auf jeden Fall eine objektorientierte, aber auch dynamisch getypte Sprache. Also hat es schon mal auch eine gewissen Ähnlichkeit, einfach über die dynamische Typisierung zu Clojure. Wobei das Programmiermodell eher doch im Wesentlichen so ist, wie bei Java. Also ohne jetzt schon viel mit Groovy gemacht zu haben würde ich stark davon ausgehen, dass Groovy-Programme von der Struktur her sicherlich grundsätzlich nicht all zu stark sich unterscheiden von Java-Programmen. Also auch dort wird man irgendwie Klassen schreiben und Objekte instanziieren und die Klassen werden Methoden haben und ähnliches, sodass es schon ein relativ großer Unterschied zu Clojure ist – einfach in der Art und Weise wie man die Programme gestaltet. Bei Scala: Scala ist ja erst mal statisch getypt auch, so wie Java.

Stefan Tilkov: Sehr statisch getypt.

Philipp Schirmacher: Sehr statisch, sehr mächtiges Typ-System. Genau, also da unterscheidet es sich deswegen schon mal relativ stark von Clojure. Wobei was das Programmiermodell angeht, Scala ja eigentlich beides unterstützt. Also man kann sowohl objektorientiert programmieren, wie in Java, und kann auch funktional programmieren, wie in Haskell, Clojure, wie auch immer. Ja, also Scala bietet da an der Stelle sicherlich erstmal mehr, ist aber dafür natürlich dafür auch sehr – ja also es hat nicht so diesen minimalistischen Ansatz, der bei Clojure eigentlich oder den zumindest ich bei Clojure sehr schön finde. Das ist natürlich immer Geschmackssache.

Stefan Tilkov: Niemand würde Scala vorwerfen, minimalistisch zu sein. Das passiert nicht.

Philipp Schirmacher: Bei Scala muss ich mir schon genau überlegen, welche Features man wie benutzen möchte, hat aber natürlich auch sehr viele Möglichkeiten.

Stefan Tilkov: Wie sieht es bei den Datenstrukturen aus? Du hast vorhin generische Datenstrukturen erwähnt. Wie ist da die Unterscheidung der unterschiedlichen Sprachen?

Philipp Schirmacher: Also ein wichtiges Feature bei Clojure ist, dass die Datenstrukturen, die man üblicherweise benutzt, alle unveränderlich sind. D.h., wenn man z.B. einen Vektor hat und fügt dort ein neues Element hinzu, wird eben nicht der bestehende Vektor verändert, sondern es wird ein neuer Vektor erzeugt, mit eben einem weiteren Element. Das ist was, was so zumindest in Java erstmal nicht unterstützt wird. Die Collection da sind ja üblicherweise veränderbar. In Scala ist es so, dass die Collection – da gibt’s beides. Es gibt einerseits veränderbare Collection, es gibt aber auch in Scala unveränderbare, also immutable Collections. Wobei es glaube ich auch so war, dass tatsächlich dieses – also die unveränderlichen Collections in Clojure sind wohl relativ clever implementiert und tatsächlich nach einem Paper implementiert, was am Lehrstuhl von Martin Odersky erstellt worden ist. Also sozusagen in der Scala-Welt. Dann wurde es in Clojure implementiert. Mittlerweile soweit ich weiß auch ähnlich in Scala. Also genau, in Scala hat man im Prinzip auch diese unveränderlichen Datenstrukturen.

Stefan Tilkov: Diese – was du gerade geschildert hast – diese unveränderlichen Datenstrukturen, das klingt ein bisschen so, als wäre das rein akademisch zwar ganz nett – passt zu dem Lehrstuhl-Bezug von grad – aber in der Praxis völlig untauglich. Klingt so, als könnte ich das nie im Leben in einem ersthaften Programm benutzen. Wenn ich eine Millionen Elemente in einem Vektor drin habe, dann werde ich da nicht, wenn ich das eine Millionen und erste Element hinzufüge, den ganzen Vektor kopieren wollen.

Philipp Schirmacher: Genau, passiert auch nicht. Also es ist nicht so, dass alles komplett kopiert wird, sondern es ist so, dass die sogenanntes structure sharing implementiert haben. Also es wird nur ein geringer Teil der Daten tatsächlich kopiert und diese beiden Vektoren, die man hat, teilen sich letztendlich einen Großteil der Elemente. Was eben deshalb kein Problem ist, weil ja beide unveränderlich sind.

Stefan Tilkov: D.h., man hat sozusagen die Illusion, dass es kopiert wurde, man hat das pure der Funktion. Die Funktion bekommt einen Vektor mit einer Millionen Elementen rein und gibt einen anderen Vektor mit einer Millionen und einem Elementen raus. Das sieht so aus, als wäre alles eben komplett verändert, aber in Wirklichkeit passiert unter der Haube eben doch das, was man aus Effizienzgründen erwarten würde.

Philipp Schirmacher: Genau.

Stefan Tilkov: Ok. Wofür würdest du Clojure denn einsetzen? Also was wären denn Bereiche, in denen man mit Clojure ganz besonders glänzen kann? Wofür eignet es sich besonders?

Philipp Schirmacher: Also wofür ich es sehr schön finde ist speziell auch für Web-Applikationen. Einfach schon grundsätzlich vom Modell her finde ich, eine Web-Applikation ist irgendwie was, was einen HTTP-Request bekommt und daraus eine HTTP-Response erzeugt. Also passt eigentlich schon mal ganz gut zu diesem funktionalen Modell. Und auch das Arbeiten mit generischen Datenstrukturen finde ich da echt vorteilhaft. Weil insbesondere, wenn man mit JSON arbeitet, hat man das ohnehin. Also im Web arbeitet man sowieso sehr viel mit eben diesen generischen Datenstrukturen und deswegen ist der Weg von diesem JSON, das man rein bekommt, zu Clojure-Datenstrukturen nicht besonders weit. Also in Java ist es teilweise schwierig oder zumindest etwas aufwendiger aus dem JSON ein enstprechendes Objektmodell oder das auf ein entsprechendes Objektmodell zu mappen. In Clojure ist der Weg sehr kurz, das spart einem schon mal eine ganze Menge Arbeit. Das finde ich ganz nett. Ja, grundsätzlich ist es so, dass Clojure – dadurch, dass es auf der JVM läuft – sich eigentlich überall einsetzen lässt, wo man Java auch einsetzen kann. Also es ist eher so eine Geschmacksfrage, wo man es einsetzen möchte. Grundsätzlich möglich ist es eigentlich fast überall. Was ich jetzt z.B. auch mal mit Clojure gemacht hatte, war in einem Projekt – das ist jetzt schon ein, zwei Jährchen her – haben wir Kundenprozesse, also Supportprozesse für Geschäftskunden optimiert. Es waren so 20–30 Prozesse, die es da gab, die in 24 Schritten jeweils bestimmte Messungen durchgeführt haben. Also der Kunde hat gesagt, dieses oder jenes an meinem Produkt funktioniert nicht. Dann wurden Messungen durchgeführt oder bestimmte Fragen gestellt: Leuchtet dieses Lämpchen? – Wenn ja, dann du dies; wenn nein, dann du das. Das war alles implementiert mit einem Workflow-System, was so ein bisschen schwerfällig war und wo die Entwicklung von neuen Prozessen sehr teuer war, sodass man da eben diese Prozesse vereinfachen wollte um letztendlich die Implementierung zu vereinfachen. Also man hat im Prinzip das tun wollen, was man sonst oft vermeidet: man wollte aus diesen 20 nicht so großen Prozessen, am liebsten eigentlich nur ein, zwei fette Prozesse machen, die alles auf einmal können. Und um – also das war eigentlich ein Beratungsprojekt, also wir haben da nichts wirklich implementiert, aber eben vorgeschlagen, wie diese Prozesse neu strukturiert werden sollen und haben dazu eben einen Simulator mit Clojure geschrieben, wo man eben diese Prozesse abbilden konnte und simulieren konnte, wie sich die Prozesse in bestimmten Input-Situationen eben verhalten, um sicherzustellen, dass diese ein oder zwei großen Prozesse sich tatsächlich in allen Situationen wirklich genau so verhalten, wie es die 20 spezifischen Prozesse auch getan hätten. Also auch so kleine Tools, die jetzt vielleicht nicht irgendwie produktiv gehen, sondern nur am Rande eines Projekts entstehen – auch sowas finde ich so einen ganz schönen Anwendungsfall, wo man so eine neue Sprache mal ausprobieren kann ohne gleich all zu viel kaputt zu machen, im schlimmsten Fall.

Stefan Tilkov: Das ist auch immer eine gute Taktik für neue Sprachen, neue Umgebungen, die erstmal so ein bisschen in den weniger kritischen Bereichen eingesetzt werden, wo es erstmal nicht weh tut und dann breiten die sich schon von ganz alleine aus. Die typischen internen Tools und internen Tracker.

Philipp Schirmacher: Typische Guerilla-Taktik.

Stefan Tilkov: Gut, können wir sonst noch was erzählen, was wir mit Clojure produziert haben? So entsetztlich viel ist es nicht, kann man auch offen sagen. Also wir haben deutlich mehr in Java und Ruby und wir haben auch durchaus eine relevante Menge Scala, dazu machen wir sicher auch mal einen seperaten Podcast. Aber fällt dir nochwas ein, was wir mit Clojure betrieben haben.

Philipp Schirmacher: Ja, wir haben intern noch so ein paar Web- Applikationen, die wir damit laufen haben. So einen kleinen Twitter-Klon, den wir intern benutzen.

Stefan Tilkov: Der ist übrigens sogar Open-Source, unser Twitter-Klon. Public bei GitHub, verlinken wir in den Shownotes.

Philipp Schirmacher: Ja, wir haben jetzt auch ein Produkt, was wir als Service vermutlich bald anbieten werden, wo auch eine Komponente in Clojure entwickelt worden ist. Ja, also es sind auch relativ viele Web- Applikationen, die wir eigentlich so damit gebaut haben. Genau, ja und privat ist es bei mir mittlerweile so, dass wenn ich irgendwas programmiere, was irgendwie Spaß machen soll, dann nehme ich einfach aus persönlicher Vorliebe gerne Clojure dafür, um irgendwelche Roboter zu programmieren oder irgendwelche Raspberry Pis. Wobei beim Raspberry Pi ist dann die JVM vielleicht nicht so das optimale Mittel, weil es einfach sehr viele Ressourcen braucht. Aber auch da kann man schön mit Clojure spielen.

Stefan Tilkov: Was wir nicht erwähnt haben ist ClojureScript. Kann man vielleicht für die JavaScript-Fraktion auch noch kurz erwähnen oder für die Browser-Fraktion kurz erwähnen?

Philipp Schirmacher: Ja, klar. Das ist z.B. auf einem Raspberry Pi sicherlich auch eine Möglichkeit. ClojureScript ist im Prinzip eine Implementierung des Clojure-Compilers, die nicht JVM-Bytecode erzeugt, sondern eben JavaScript. Und dadurch hat man eben die Möglichkeit nochmal eine ganz andere Plattform zu adressieren, also z.B. node.js – also Google V8 – oder auch im Browser lässt sich das damit sehr schön einsetzen. Also man kann dann sozusagen full stack LISP programmieren. Sehr schön, ja.

Stefan Tilkov: Web-Anwendungen, full stack LISP. Wenn man starten will, hast du gute Tipps, gute Startpunkte, wenn man sich mit Clojure näher beschäftigen möchte?

Philipp Schirmacher: Also es gibt natürlich schon so ein paar Bücher zu dem Thema, die man lesen kann. Von O’Reilly z.B. Wobei es meiner Meinung nach gar nicht nötig ist sich sofort irgendwie ein Buch zu kaufen und sich damit auseinander zu setzen, sondern man findet auch im Internet eine Menge Artikel und vor allem auch so ein paar Seiten, wo man das direkt mal ausprobieren kann. Eine ist z.B. 4clojure.com – soweit ich weiß, haben wir dann bestimmt auch in den Shownotes – wo man, im Prinzip ist das einfach so eine Aufgabensammlung von sehr leichten bis sehr schwierigen Aufgaben, die man in Clojure lösen kann und man hat direkt im Browser eine REPL, wo man direkt den Code eingeben und auswerten kann. Ja, das ist eigentlich sehr schön um da so ein bisschen rein zu kommen, einfach so ein bisschen damit rumzuspielen. Ansonsten lohnt es sich auch einfach mal das Build-Tool zu installieren und ja ein kleines Projekt zu starten. Also ich finde es immer ganz gut, wenn man einfach irgendwie sich irgendeine Applikation überlegt, die man gerne hätte oder die man gerne bauen würde und dann einfach mal versucht mit einer anderen Sprache an das Thema ran zu gehen. Dann ist die Motivation immer ein bisschen größer, als wenn man einfach nur Spielereien macht.

Stefan Tilkov: Fibonacci, oder so. Kann man auch mal machen.

Philipp Schirmacher: Kann man auch mal machen, ja.

Stefan Tilkov: Letzte Frage: Was ist die IDE deiner Wahl?

Philipp Schirmacher: Für Clojure tatsächlich Emacs, wobei ich jetzt auch nicht der „Schon-immer-Emacs-User“ bin, sondern eigentlich erst durch Clojure auch dazu gekommen bin, erst mit Clojure damit angefangen hab. Wobei, also bei mir ist das jetzt schon zwei-drei Jährchen her, da war Emacs eigentlich auch noch so die einzige wirklich ausgereifte Möglichkeit, Clojure-Code zu schreiben. Vim ging auch noch, ähnlich zugänglich. Aber mittlerweile sind eigentlich auch die PlugIns für IntelliJ, Eclipse usw. wesentlich besser geworden und man kann auch problemlos damit entwickeln. Viele Leute benutzen dann letztendlich Emacs, weil es sich sehr gut eignet um Clojure-Code zu schreiben, aber es ist keine Voraussetzung. Also man kann auch mit jedem beliebigen anderen Tool, man kann auch einfach irgendeinen Texteditor benutzen, also irgendeinen anderen Texteditor benutzen.

Stefan Tilkov: Ich glaube, das spannende ist immer, dass irgendeine REPL- Integration da sein muss. Haben glaube ich auch alle diese IDEs, haben das auch entsprechend und für Vim gibt’s das auch, dass man eben das mischen kann, dass man editieren kann oder explorativ damit rumspielen kann. Sicherlich ein ganz spannender Punkt.

Philipp Schirmacher: Genau.

Stefan Tilkov: Ok, berühmte letzte Worte? Noch eine abschließende Aussage zu Clojure?

Philipp Schirmacher: Ist sehr anders, macht aber viel Spaß und ja lohnt sich auf jeden Fall mal, sich das anzugucken. Man sollte sich nicht zu viele Sachen da abgucken und dann gleich in jedem Java-Projekt vielleicht umsetzen. Also so wie du sagtest, irgendwie generische Datenstrukturen dann nur noch zu benutzen in Java ist vielleicht nicht das, was man sich abgucken sollte, aber trotzdem kann man sich da eine Menge interessante Sachen anschauen, einiges dabei lernen, ist auf jeden Fall ein Blick wert.

Stefan Tilkov: Alles klar. Gut, vielen Dank Philipp und bis zum nächsten Mal.

Philipp Schirmacher: Bitteschön, danke sehr.

Senior Consultant

Philipp Schirmacher arbeitet als Senior Consultant bei INNOQ. Sein Schwerpunkt liegt auf der Entwicklung von verteilten Systemen in agilen Teams. Darüber hinaus interessiert er sich insbesondere für Event-getriebene Architekturen, Domain-driven Design und das Web.

In Memoriam ∞ CEO & Principal Consultant

Stefan Tilkov war Geschäftsführer und Principal Consultant bei INNOQ Deutschland, wo er sich vorwiegend mit der strategischen Beratung von Kunden im Umfeld von Softwarearchitekturen beschäftigte. Er war Autor des Buchs „REST und HTTP“, Mitherausgeber von „SOA-Expertenwissen“ (beide dpunkt.verlag), Autor zahlreicher Fachartikel und häufiger Sprecher auf internationalen Konferenzen.

Wir trauern um Stefan.