Die Welt wartet nicht. Der Wasserkocher wartet nicht, wenn wir den Tee nicht finden, und die Ampel wird grün, während wir noch am Radio drehen. Die Zeit – und mir ihr der Zustand unserer Umgebung – steht nie still. Durch die Erkenntnisse der Relativitätstheorie müssen wir diese Aussage in Anwesenheit sehr großer Massen und bei hohen Geschwindigkeiten relativieren, aber unser Alltagsleben wird von ständigem Wandel bestimmt. Stillstand in der Zeit begegnen wir eher in fantastischen Geschichten von Dornröschen bis Star Trek; und in den Konzepten vieler Computerprogramme, was angesichts der Modernität dieses Umfelds überrascht. Diese Tatsache lässt sich mit der Entwicklung der Computer erklären, aber nicht mehr tolerieren.
Für die Modellierung einer auch außerhalb eines Programmabschnitts weiterschreitenden Zeit brauchen Programmierer neue Werkzeuge. Dabei haftet neuen Werkzeugen oft der Makel der noch nicht erwiesenen Stabilität an, was die Verbreitung der Neuerungen behindert. Bei diesem Problem kann der Rückgriff auf erprobte, verbreitete Technologien helfen. Clojure ist eine junge Programmiersprache, die primär entwickelt wurde, um die Arbeit mit parallel ablaufenden Programmteilen und deren Zugriff auf gemeinsam verwendete Ressourcen zu vereinfachen. Clojure nimmt eine strikte Trennung von Werten und Zuständen vor und kombiniert verschiedene Technologien zu einem Gesamtbild, das einen sicheren Umgang mit sich ändernden Zuständen erlaubt. So wird das Fortschreiten der Zeit außerhalb eines Programmteils, sichtbar eben an der Veränderung eines Zustands, eine handhabbare Größe.
Nach unserer Auffassung können wir das Entstehen von Clojure mit einem Blick auf die Geschichte erklären. Die zentralen Fragen sind sicherlich:
Im Jahre 1965 formulierte Gordon Moore seine Vermutung, dass sich die Komplexität integrierter Schaltkreise in regelmäßigen Abständen verdoppelt. Diese Vermutung, die heute als „Moore’s Law“ bekannt ist, wurde in der Zwischenzeit bezüglich der Zeiträume angepasst und mehrfach umgedeutet. Heute messen viele ihr die Bedeutung zu, dass sich die Geschwindigkeit unserer Computer in regelmäßigen Abständen verdoppelt. Unabhängig von der Richtigkeit dieser Ansicht hat das dazu geführt, dass Programmierer manchmal einfach auf ausreichende Performance beim Projektende gehofft haben. Zur Begründung dieser Annahme wurde die höhere Verarbeitungsgeschwindigkeit der zukünftigen Maschine herangezogen. Computer werden schneller.
Taktrate
Deren Einführung für den Massenmarkt wird ungefähr auf das Jahr 2006 datiert. Auf modernen Betriebssystemen arbeiten viele Programme gleichzeitig und diese lassen sich leicht auf mehrere Prozessoren oder Prozessorkerne verteilen, wodurch sich der Computer für den Anwender schneller anfühlt.
Threads
Der Begriff Concurrent Programming, meist als „nebenläufige Programmierung“ ins Deutsche übersetzt, bezeichnet die Entwicklung ebensolcher Multithreading-Programme, die auf gemeinsame Ressourcen zugreifen müssen.
Alle wichtigen Programmiersprachen wurden um Mechanismen zum Concurrent Programming erweitert. Jedoch oft mit einigen Problemen:
Auf diese Probleme kann Clojure Antworten geben:
Damit gibt Clojure Programmierern ein geeignetes Werkzeug an die Hand, um das dritte Problem selbst zu lösen.
Von 1991 bis 1992 wurden die Grundlagen entwickelt, die später zu Java führen sollten. Dabei wird unter dem Begriff „Java“ oft die Gesamtheit aus der Programmiersprache Java und der Java-Runtime-Umgebung inklusive der Java Virtual Machine (JVM) verstanden. Schon sehr früh haben die Entwickler die Entscheidung getroffen, eine virtuelle Maschine zu implementieren, die die Details des darunter liegenden Betriebssystems verbirgt. Eine Folge dessen war, dass das Ausführen verschiedener Threads auch ohne Unterstützung des Betriebssystems in Form sogenannter Green Threads vorausgesetzt werden konnte. Dadurch hat sich Multithreading in Java schnell durchgesetzt. Bereits in den ersten Java-Versionen waren Werkzeuge für die separate Ausführung von Programmteilen vorhanden. Asynchrone BearbeitungZu jener Zeit waren jedoch Mehrprozessorsysteme nicht verbreitet, und so lag der Fokus auf der asynchronen Bearbeitung von Aufgaben.
Heute sind Green Threads weitestgehend durch native Threads der Betriebssysteme abgelöst, die in der Regel auch performanter sind. Dies erlaubt es der JVM, sich auf die immer zahlreicher vorhandenen Prozessorkerne zu verteilen. Infolgedessen tritt aber – wie schon erwähnt – das gleichzeitige Ausführen der Programmteile an die Stelle der sukzessiven Verarbeitung separater Aufgaben. Somit wird eine immer feiner granulierte Koordination der Zugriffe auf geteilte Ressourcen notwendig. Dem trägt die Entwicklung von Java als Sprache und als Plattform durch neue Sprachbestandteile Rechnung [21].
Plattformunabhängigkeit
Optimierung zur Laufzeit
Weitere Vorteile
Die Programmiersprache Java hat recht schnell große Verbreitung gefunden, und heute laufen viele unternehmenskritische Anwendungen auf der JVM. In den Betrieben wurde viel in Java-Programme investiert, und eine Vielzahl von qualitativ hochwertigen Bibliotheken ist frei verfügbar.
Alternative Sprachen
Clojure ist ein weiterer Spieler auf diesem Feld, und der Einsatz der JVM ist dabei kein Detail der Implementation, sondern eine explizite Design-Entscheidung. Im Unterschied zu Python und Ruby, die von einer nativen Implementation auf die JVM portiert wurden, ist Clojure von vornherein für die JVM konzipiert, so dass Clojure ohne Konflikte das Typsystem von Java übernehmen kann.
Somit erhält Clojure durch die JVM als Plattform ein enormes Startkapital – Typen, Speicherverwaltung, Plattformunabhängigkeit und Bibliotheken –, was die Stabilität erhöht und die Adaption erleichtert.
Lisp, genauer die Familie der Lisp-artigen Programmiersprachen, blickt auf eine lange Vergangenheit zurück. Als Geburtsjahr wird 1958 betrachtet, als John McCarthy die erste Sprache zum LISt Processing spezifizierte. Einen wichtigen Meilenstein markiert das Jahr 1994, in dem Common Lisp, der vermutlich wichtigste Vertreter der Lisp-Familie neben Scheme, zum ANSI-StandardANSI-Standard erhoben und damit ein etwa zehn Jahre währender Prozess der Standardisierung vollendet wurde.
Nach Meinung vieler Anwender bedarf dieser Standard mittlerweile einer Modernisierung, da Bereiche, die zu dem Zeitpunkt noch keine so wichtige Rolle spielten, wie sie es heute tun, nicht spezifiziert sind. Insbesondere das Fehlen einer Spezifikation für Multithreading fällt im Zusammenhang mit Clojure auf.
Lisp besitzt eine einfache Syntax und verfügt mit seinem Makrosystem über Möglichkeiten, die bis heute in anderen Programmiersprachen nicht gängig sind. Die einfache Syntax gepaart mit einem sehr kleinen Sprachkern führen dazu, dass simple Interpreter leicht zu schreiben sind. Infolgedessen entstehen immer mal wieder neue Lisp-Dialekte. Diese werden jedoch von der Lisp-Gemeinde sehr kritisch beäugt und schaffen selten eine nennenswerte Verbreitung.
Ankündigung von Clojure
Hier war ein neuer Lisp-Dialekt wie aus dem Nichts erschienen, der im Gegensatz zu vielen anderen Versuchen vor allem durch die gut begründeten Design-Entscheidungen und verwendeten Technologien bereits im ersten Jahr [31] eine aktive Entwickler- und Anwendergemeinde aufbauen konnte.
Funktionale Programmierung
Manche betrachten den Lisp-Charakter kritisch, weil er vor allem durch die ungewohnte Syntax potenzielle Entwickler abschreckt. Die Menge an Kommentaren zu runden Klammern spricht eine deutliche Sprache. Wir gehen gegen Ende des Abschnitts 2.2 näher darauf ein. An dieser Stelle nur so viel: Die Syntax ist ein gewünschtes Feature und ein großer Vorteil. Wer sich für die technischen Konzepte interessiert, wird sich von einer ungewohnten Syntax ohnehin nicht abschrecken lassen.
Von Lisp profitiert Clojure also durch das Makro-System, den schlanken Sprachkern, die einfache Syntax und den funktionalen Charakter.
Nach unserer Meinung kann Clojure ohne Umschweife überall dort eingesetzt werden, wo Java eingesetzt wird, besonders wenn Concurrency bereits eine Rolle spielt oder es sich abzeichnet, dass es dazu kommen wird. Die technischen Voraussetzungen sind gegeben, vor allem durch das sehr gute Zusammenspiel von Clojure und Java, und die Lizenz erlaubt den Einsatz. Mit diesem Buch wollen wir daher in erster Linie die Java-Programmiererinnen und -Programmierer ansprechen. Dazu versucht dieses Buch mit einer oberflächlichen Einführung in Lisp den Kulturschock durch die Präfixsyntax abzumildern, um dann direkt in die Beschreibung der Sprache einzusteigen. Wer bereits über Erfahrungen mit Lisp verfügt, dürfte sich ohnehin in vertrauter Umgebung befinden, auch wenn Clojure für erfahrene Lisp-Programmierer die eine oder andere Überraschung bereithält.
Dieses Buch vermittelt insgesamt einen umfassenden Einstieg in die Programmiersprache Clojure, so dass es sich auch für jeden anbietet, der ohnehin Clojure oder immer schon mal ein Lisp lernen wollte.
Zusammenfassend richtet sich dieses Buch an:
Ein Wort der Warnung ist angebracht. Die Benutzung von Lisp-artigen Programmiersprachen kann süchtig machen und/oder die Entwicklung mit anderen Sprachen unangenehm bis schmerzhaft erscheinen lassen.
Sollten Sie sich nach Lektüre dieses Buchs und ggf. einiger Projekte mit Clojure nicht mehr in der Lage sehen, mit anderen Sprachen zu arbeiten, können wir dafür keine Verantwortung übernehmen.
Während der Erstellung dieses Buchs haben die Entwickler am 31. Dezember 2009 Version 1.1, eine moderate Weiterentwicklung der Version 1.0, und am 19. August 2010 Version 1.2, die weitgehende Änderungen enthält, veröffentlicht. Die Version 1.2 war auch Grundlage aller Beispiele und Betrachtungen, die unter die Haube auf die Implementation schauen. Da die Version 1.2 zum Zeitpunkt der Veröffentlichung dieses Buches noch sehr frisch ist, haben wir darauf geachtet, dass unsere Beispiele weitestgehend auch in Clojure 1.1 lauffähig sind.
Auch die eng verwandte Standardbibliothek Clojure Contrib hat ein Release Version 1.2 erfahren, das die vollständige Kompatibilität mit Clojure 1.2 anstrebt. Diese Version wurde ebenfalls verwendet.
An das einleitende erste Kapitel schließt sich in Kapitel 2 eine umfangreiche Einführung in die Sprache Clojure an. Die Einführung beginnt mit einer Beschreibung der Eigenschaften von Clojure und einem Grundkurs für Lisp-Einsteiger. Auch erfahrene Lisper sollten diesen Abschnitt zumindest querlesen, da Clojure gegenüber bekannten Lisps einige Änderungen einführt. Nach den Grundlagen folgt die Einführung der Sprache beginnend bei den Datentypen bis zu der detaillierten Betrachtung, welchen Weg Clojure-Code bis zum Resultat nimmt.
Kapitel 3 widmet sich dem Concurrent Programming und zeigt, welche Eigenschaften Clojure für diese Aufgabe gut geeignet erscheinen lassen. Der Inhalt dieses Kapitels dürfte für viele Anwender ein wichtiger Grund sein, sich mit Clojure zu beschäftigen. Wer bereits mit Lisp und funktionaler Programmierung vertraut und auf dieses Kapitel sehr gespannt ist, kann durchaus nach den Grundlagen direkt dorthin blättern. Das Kapitel endet mit einem größeren Beispiel, das nicht nur Clojures Technologien für die parallele Programmierung zeigt, sondern auch beschreibt, wie Nebenläufigkeit zu anderen Algorithmen führen kann.
Die Interaktion mit Java ist Thema des Kapitels 4. Im Gegensatz zu den meisten anderen Quellen, die diese Interaktion behandeln, wird jedoch nicht nur beschrieben, wie Java aus Clojure verwendet werden kann. Auch die Gegenrichtung, die in manchen Anwendungsfällen durchaus relevant sein kann, wird betrachtet. Auch dieses Kapitel enthält ein umfangreiches Beispiel, das die Verbindung von objektorientierter Bildschirmausgabe und funktionalem Datenmodell demonstriert.
Im darauf folgenden Kapitel 5 kommen mit Protocols und Datatypes zwei Merkmale von Clojure zur Sprache, die sich mit der abstrakten Beschreibung von Schnittstellen und deren konkreter Umsetzung durch Datentypen befassen. Diese haben das Zeug, sich zu wesentlichen Eigenschaften zu entwickeln, haben sich jedoch aufgrund ihres jungen Alters noch nicht weit verbreitet.
Im sechsten Kapitel werden einige ausgewählte Bibliotheken vorgestellt, die entweder im Sprachumfang von Clojure bereits enthalten oder aber in der Sammlung Clojure Contrib zu finden sind. Die Auswahl spiegelt unsere subjektive, ungefähre Einschätzung der Wichtigkeit oder Nützlichkeit der Bibliotheken wider. Durch das mittlerweile umfangreiche Angebot an für Clojure verfügbaren Bibliotheken ist die Liste natürlich nicht vollständig.
Den Abschluss bildet Kapitel 7, in dem wir ein persönliches Fazit ziehen, aber auch kritische Stimmen aus Blogs und Mailinglisten zu Worte kommen lassen. Ein Ausblick auf die kommende Entwicklung beendet schließlich dieses Buch.
In diesem Buch verwenden wir die folgenden Auszeichnungen für die beschriebenen logischen Elemente:
Dieser Abschnitt enthält für den Einsteiger eher verwirrende Hintergrundinformationen und kann beim ersten Lesen übersprungen oder nur überflogen werden.
REPL und Definitionen
Druck
Threads
Wie heißt es so schön im englischen Sprachgebrauch: Your mileage might vary.
Emacs, SLIME
Einige Konstrukte, etwa Funktionsaufrufe, werden im Buch durch eine formale Syntaxbeschreibung eingeführt. Diese hat in der Regel die Form:
Dabei werden folgende Schreibweisen verwendet, die im Lisp-Umfeld durchaus üblich sind:
Dieses Buch wurde auf Computern getippt, auf denen verschiedene Varianten von Linux (Debian, Ubuntu) sowie OSX liefen. Als Satzsystem kam pdfTEX 3.1415926-1.40.10 getarnt als LATEX aus dem Paket TEXLive 2009 zum Einsatz. Beim Tippen geholfen haben GNU Emacs in Version 23.1.1 [66] sowie AUCTEX 11.85 [40]. Die Verteilung der Arbeit wurde dank Git [71] geschultert und die Abbildungen wurden mit XFig [64] und Graphviz [18] erstellt. An dieser Stelle vielen Dank den unzähligen Entwicklerinnen und Entwicklern, die diese Programme dahin gebracht haben, wo sie heute sind.
Zunächst muss unser Dank Rich Hickey gelten, dem es mit der Entwicklung von Clojure gelungen ist, ein bedeutendes neues Lisp zu schaffen, dessen Studium sich aufgrund seiner konzeptionellen Eigenschaften lohnt und dessen produktiver Einsatz durch die technischen Grundlagen schon heute möglich ist. Das ist eine beeindruckende Leistung.
Etwas konkreter und von grundlegender Bedeutung für dieses Buch ist der Einfluss von Dr. Stefan Koospal von der mathematischen Fakultät der Universität Göttingen . Er hat es durch seine überzeugende Art geschafft, einen der Autoren zu einem Vortrag über Clojure beim SourceTalk 2009 [41] zu bewegen. Dieser Vortrag war der entscheidende Einfluss für Dr. Michael Barabas vom dpunkt.verlag [13], Kontakt aufzunehmen. Auch ihm und dem Team vom dpunkt.verlag – Nina Lötsch, Annette Schwartz, Nadine Thiel und Vanessa Wittmer – sei unser Dank hiermit ausgedrückt.
Bei der Arbeit am Buch selbst haben auch viele Menschen geholfen. Allen voran unsere Korrekturleser (in alphabetischer Reihenfolge): Meikel Brandmeier, Claus Brunzema, Detlev Evers, Hans Hübner, Karl-Friedrich Kamphausen, Alex Schröder, Stefan Tilkov und Serkan Zeybek. Aber auch die vielen guten Geister in der Diskussionsgruppe und im IRC-Channel, die uns bei diversen Detailfragen helfen konnten. Der Code in Abschnitt 3.7.1, der die Verwendung der Historie in Refs demonstriert, stammt von Chris Houser. Die Kommentare des Originals [36] wurden für dieses Buch ins Deutsche übersetzt. Dafür noch einmal speziellen Dank; schließlich sollte er eigentlich an seinem Clojure-Buch arbeiten.
Für das Vorwort gilt unser Dank Prof. Dr. Volker Ahlers, dessen Erfahrung und Eloquenz für einen glanzvollen Einstieg sorgen. Für das Geleitwort bedanken wir uns bei Hans Dockter, dem mit dem Build-System Gradle [11] ein großer Wurf gelungen ist
Indirekt durch ihre Unterstützung und Geduld haben unsere Familien und Freunde ebenfalls zum Entstehen dieses Buchs beigetragen. Sie haben uns zudem – ebenso wie viele andere – immer wieder gut zugeredet und vorangetrieben.
Abschließend sei dem stolzen Papa gestattet, seiner Tochter Elodie besonders zu danken, die die Vorlage für die Grafik des Titelbildes im Alter von nur fünf Jahren gemalt hat. Das Original hat eine Höhe von 100 cm.