1  Einleitung

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.

 1.1  Geschichte
  1.1.1  Mehr Moore
  1.1.2  Java
  1.1.3  Lisp
 1.2  Für wen ist dieses Buch?
 1.3  Warnung
 1.4  Verwendete Version von Clojure
 1.5  Über dieses Buch
  1.5.1  Aufbau
  1.5.2  Typografische Richtlinien
  1.5.3  Zu den Beispielen
  1.5.4  Formale Syntaxbeschreibungen
  1.5.5  Produktionshinweise
 1.6  Danksagungen

1.1  Geschichte

Nach unserer Auffassung können wir das Entstehen von Clojure mit einem Blick auf die Geschichte erklären. Die zentralen Fragen sind sicherlich:

1.1.1  Mehr Moore

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

Vor allem die Erhöhung der Taktrate, in der Vergangenheit vorrangig für den Geschwindigkeitszuwachs verantwortlich, ist jedoch an ihre Grenzen gestoßen. Die hohe Taktfrequenz der Prozessoren erzeugt mittlerweile so viel Abwärme, dass sie nicht mehr effizient voranzutreiben ist. Heutige Computer erhöhen ihre Gesamtgeschwindigkeit durch den Einsatz von Mehrkernprozessoren.

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

Damit wird jedoch nur ein Ensemble von Programmen optimiert, ein einzelnes Programm kann von mehreren Kernen in der Regel nicht ohne Anpassungen profitieren. Dazu muss das Programm in Einzelteile, Threads, zerlegt werden, die parallel verarbeitet werden können. Multithreading ist kein neues Phänomen. Mit steigender Anzahl Kerne in den aktuellen Prozessoren gewinnt Multithreading jedoch immer weiter an Bedeutung. Jetzt erst laufen die Threads wirklich gleichzeitig ab, was zu konkurrierenden Zugriffen auf geteilte Ressourcen führt.

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:

  1. In den meisten Fällen ist die Behandlung der typischen Probleme aus dem Concurrent Programming nur eine Erweiterung der Sprache.
  2. Die Verwaltung des Zugriffs auf Ressourcen, die sich die Threads teilen, ist entweder kompliziert oder nicht gut skalierbar.
  3. Der überwiegende Teil der Programmierer ist gedanklich noch nicht in diesem Szenario angekommen.

Auf diese Probleme kann Clojure Antworten geben:

  1. Clojure wurde primär für Concurrency entwickelt, sein Design baut auf dafür wichtigen Konzepten auf.
  2. Die Vereinfachung des Zugriffs auf gemeinsame Ressourcen ist ein wichtiges Ziel von Clojure und verwendet eine Kombination aus verschiedenen Technologien.

Damit gibt Clojure Programmierern ein geeignetes Werkzeug an die Hand, um das dritte Problem selbst zu lösen.

1.1.2  Java

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

Eine virtuelle Maschine als Plattform bietet noch weitere Vorteile. Offensichtlich ist die Plattformunabhängigkeit – im Sinne von Hardware und Betriebssystem – des eigenen Codes. Programme laufen überall dort, wo die Plattform vorhanden ist. Anwendungsentwickler brauchen sich idealerweise nicht mehr um Spezifika des Betriebssystems zu kümmern.

Optimierung zur Laufzeit

Eine solche Plattform ist aber auch in der Lage, zur Laufzeit ein Programm zu analysieren und wichtige Programmteile zu optimieren. Im Falle der JVM obliegt das der HotSpot-Technologie.

Weitere Vorteile

Zusätzlich liefert die Plattform ein Typsystem und die Speicherverwaltung mit.

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

Seit Version 6 von Java (2006) rücken durch die Einführung der Scripting API (JSR 223) alternative Sprachen für die Java-Plattform in den Fokus der Plattformentwicklung. Solche Sprachen sind beispielsweise alternative Implementationen der beliebten Interpreter-Sprachen Python und Ruby, aber auch ganz neue Sprachen wie Scala und Groovy. All diesen Sprachen ist gemein, dass sie auf der stabilen und performanten JVM laufen und sich die enorme Anzahl von Bibliotheken im Java-Umfeld zu eigen machen können.

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.

1.1.3  Lisp

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

Vermutlich wurde daher die Ankündigung von Rich Hickey, zuvor als Entwickler von zwei Bibliotheken in Erscheinung getreten, die den Brückenschlag von Common Lisp zu Java versuchten (Foil [30] und jFli [34]), in der Nacht vom 16. auf den 17. Oktober 2008 wenig beachtet.

  Hello,
  
  As someone interested in Foil or jFli, I thought you might
  want to know about my latest project - Clojure, a dialect
  of Lisp for the JVM. It’s currently alpha, but fairly
  complete. I’m looking for some feedback from some intrepid
  folks willing to kick the tires.
  
  http://clojure.sourceforge.net/
  
  Please use the Google group for feedback:
  
  http://groups.google.com/group/clojure
  
  Regards and thanks,
  
  Rich

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

Mit Lisp verbinden viele Programmierer auch die funktionale Programmierung, wenngleich moderne Lisps die funktionale Programmierung nur als ein mögliches Paradigma erlauben. Unveränderliche Datentypen und Funktionen ohne Nebeneffekte sind wichtige Errungenschaften der funktionalen Programmierung, die zu einer deutlich einfacheren Parallelisierung beitragen. Clojure ist eine funktionale Programmiersprache in einem ähnlich wenig strikten Sinne wie andere Lisps und vereint dieses Paradigma gekonnt mit der Objektorientierung aus dem Java-Umfeld. Clojure verfügt über keine starke Typisierung und erzwingt keine nebeneffektfreien Funktionen, ist also keine „reine“ funktionale Sprache, aber Clojures Daten sind im Default unveränderlich, Clojures Funktionen sind grundlegende Datentypen, und Lösungen in Clojure sind oft rekursiv abgebildet.

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.

1.2  Für wen ist dieses Buch?

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:

1.3  Warnung

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.

1.4  Verwendete Version von Clojure

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.

1.5  Über dieses Buch

1.5.1  Aufbau

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.

1.5.2  Typografische Richtlinien

In diesem Buch verwenden wir die folgenden Auszeichnungen für die beschriebenen logischen Elemente:

Java-Klassen
Die Namen von Java-Klassen und Interfaces werden kursiv geschrieben: java.lang.Integer. Sie tauchen im Index gesammelt unter „Klassen und Interfaces“ auf.
Funktionen
Die Namen von Clojures Anweisungen (Funktionen, Makros sowie spezielle Operatoren), zum Beispiel println, werden in Schreibmaschinenschrift gesetzt. Im Index werden Anweisungen unter „Funktionen“ gruppiert.
REPL
Auch Befehlseingaben am REPL-Prompt sind in Schreibmaschinenschrift gesetzt und zudem am Prompt user> zu erkennen.
Listings
Längere Listings, sofern sie nicht in kleineren Abschnitten erklärt werden, werden am Stück und mit Zeilennummern dargestellt.
Variablen
Die Formatierung von Variablen erfolgt identisch zu der von Funktionen, etwa *clojure-version*. Im Index werden sie unter „Variablen“ zusammengefasst.
Dateinamen
Die Namen von Dateien werden in Anführungszeichen eingefasst: source-file.clj. Sie tauchen im Index unter „Dateien und Verzeichnisse“ auf.
Bibliotheken
Die Namen von Bibliotheken und Namespaces werden wie Funktionen und Variablen gesetzt: JLine. Im Index erscheinen sie unter „Bibliotheken“.
Kommandos
Kommandos, die in einer Shell eingegeben werden, wie java oder rlwrap, erscheinen ebenfalls in Schreibmaschinenschrift. Ihre Gruppierung im Index erfolgt unter „Programme“.

1.5.3  Zu den Beispielen

Dieser Abschnitt enthält für den Einsteiger eher verwirrende Hintergrundinformationen und kann beim ersten Lesen übersprungen oder nur überflogen werden.

REPL und Definitionen

Beispiele, die einen REPL-Prompt (vgl. Abschnitt 2.3) zeigen, wurden exakt an der REPL durchgeführt. Häufig werden im Zusammenhang mit einem Beispiel an der REPL Definitionen – etwa von Funktionen oder Vars – benötigt. Diese werden gemeinsam gesetzt, ohne Prompt und mit einer Leerzeile getrennt. Die Eingabe solcher Definitionen kann wahlweise über eine separate Datei oder die REPL erfolgen.

Druck

Für den Druck wurde vor allem die Ausgabe oft umgeformt, damit die Zeilen in das Seitenlayout passen.

Threads

Bei vielen Beispielen werden Threads explizit oder implizit verwendet. In diesen Fällen wird es häufig so sein, dass eigene Versuche anders verlaufen, mehr noch, dass jeder einzelne Versuch einen anderen Verlauf nimmt. Im Rahmen der Erstellung der Beispiele für dieses Buch wurden in der Regel mehrere Versuche durchgeführt, bis ein Ablauf die zu zeigenden Effekte gut zur Geltung brachte.

Wie heißt es so schön im englischen Sprachgebrauch: Your mileage might vary.

Emacs, SLIME

Wir haben das Gespann aus Emacs, SLIME und Clojure-Mode (ebenfalls Abschnitt 2.3) in einer Version verwendet, die die Ausgabe von separaten Threads nicht im REPL-Buffer zur Anzeige brachte. Zumindest in solchen Fällen haben wir den Code mit der Standard-REPL von Clojure ausgeführt. Dennoch verwenden wir, sofern nicht ausdrücklich die REPL von Clojure zum Einsatz kommt, den Prompt von SLIME (user>) und nicht den der Standard-REPL von Clojure (user=>).

1.5.4  Formale Syntaxbeschreibungen

Einige Konstrukte, etwa Funktionsaufrufe, werden im Buch durch eine formale Syntaxbeschreibung eingeführt. Diese hat in der Regel die Form:

  (funktion argument1 optionales-arg?)

Dabei werden folgende Schreibweisen verwendet, die im Lisp-Umfeld durchaus üblich sind:

?
Ein Fragezeichen hinter einem Argument verdeutlicht, dass dieses Argument optional ist.
+
Ein Pluszeichen hinter einem Argument weist darauf hin, dass dieses Argument mehrfach, aber mindestens einmal vorkommen darf.
*
Ein Sternchen hingegen bedeutet, dass eine beliebige Anzahl gleichartiger Argumente erwartet wird und schließt den Fall ein, dass dieses Argument entfällt.
& body
Eng verwandt mit dem Sternchen ist diese Form mit dem kaufmännischen Und und einem „body“ genannten Argument. In diesem Falle sind ebenfalls beliebig viele Argumente erlaubt, allerdings werden hier Ausdrücke, also etwa andere Funktionsaufrufe, erwartet.
[], ()
Tauchen eckige Klammern auf, so sind diese auch beim Aufruf zu verwenden; es wird dann an dieser Stelle ein Vektor erwartet. Ähnlich verhält es sich mit runden Klammern, die auf eine Liste hinweisen. Alle geklammerten Ausdrücke können durch Fragezeichen und Sternchen spezifiziert werden.

1.5.5  Produktionshinweise

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.

1.6  Danksagungen

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.