Es ist wieder Winter, Zeit zum Programmieren.. ähm, naja, oder zumindest ist meine Motivation groß genug mich dazu aufzuraffen. Nachdem ich in Ein kurzer Ausflug in Scala mir Scala angeschaut habe wäre es langsam Zeit für ein neues Review.
Aber erstmal vielleicht noch ein Follow-Up zu Scala und den angesprochenen Themen: ich habe Scala seitdem nur halbherzig verfolgt. Es ist Version 2.8 rausgekommen, was quasi ein Meilenstein für die Scala-Leute ist. Die Sprache scheint sich gut zu entwickeln und das Projekt was ich in Scala geschrieben habe ist auch ganz ok verlaufen. Jedoch finde ich Scala trotz aller beteuerungen doch recht komplex. Ich hab ständig irgendwelche Sonder"syntax" wenn ich die Sprache effizient nutzen will. Ich kann natürlich auch Java in Scala schreiben. Was noch passiert ist: CLDC ist für mich tot, da ich kein Featurephone mehr nutze sondern auf ein Smartphone mit Android umgestiegen bin. Da läuft Dalvik drauf, was interessant ist weil man JVM-Bytecode dadrauf übersetzen kann. Das funktioniert mit Scala sogar ziemlich gut, habe mal zum testen ein Widget geschrieben. Das schwerste ist und war, ein brauchbares Icon zu finden. Daher nicht im Market

Aber nun zu Clojure. Wie kam ich drauf? Ich habe versucht ein relativ simples Programm zu schreiben um Log-Dateien auszuwerten. Dazu braucht man, aus Python-Sicht eigentlich nichts weiter als ein paar Regular-Expressions und ein Dict. Also dachte ich mir ok, probier ich es mit Desktop-JavaScript. Dann hat mich aber der furchtbare Stand der GLib-Bindings abgestoßen. Ein zweiter Versuch mit Scheme führte etwas weiter, ist aber an Bugs in der Regex-Engine gescheitert. Dann war ich frustriert und hatte keine Lust mehr; hab mir dann spaßeshalber mal Clojure angeschaut.
Und hey, es hat tatsächlich Spaß gemacht. Es war einfach ne Implementation zu bekommen (gibt schließlich primär die auf der JVM) und das Buch hatte ich eh schon im Regal (direkt neben dem Scala-Buch). Also hab ich mal losprogrammiert und siehe da, immutable Datenstrukturen, wunderbar (und im Gegensatz zu Scheme auch brauchbar und ausreichend angenehm nutzbar um sie tatsächlich einzusetzen)! Die ganzen funktionalen Sachen wie ``comp``, ``partial``, ``map``, ``reduce`` sind da, weitere Goodies wie ``separate`` sind in Clojure-contrib. Clojure-contrib ist eh noch so ein eigenes Ding, denn ohne Clojure-contrib macht Clojure viel weniger Spaß, denn man müsste ständig Sachen wie etwa simple Datei-IO über Java-APIs machen oder irgendwelche Hilfsfunktionen selbst implementieren.
Achja, Java. Java fühlt sich in Clojure viel abgeschirmter als in Scala an. In Scala kann man auf Java-Code zugreifen fast wie auf Scala-Objekte, in Clojure muss man Java-Sachen immer etwas besonders ansprechen. Aber das ist auch kein Wunder, denn im Gegensatz zu Scala ist Clojure nicht objektorientiert, sondern nutzt Common Lisp-artige Multimethods. Interessantes Thema, aber habe mich noch nicht sonderlich damit auseinandergesetzt. Was mir Positiv aufgefallen ist, ist die Seq-Abstraktion, was in Python ein wenig dem Sequence-Protokoll entspricht. Die "Kern"-Datentypen lassen sich alle einheitlich ansprechen. Dann gibt es noch so einige nette Features, dass etwa :keywords auch als First-Class-Funktionen durchgehen und ich mit ihren auf Dicts zugreifen kann, ebenfalls dass Dicts wie Funktionen aufgerufen werden können. Insgesamt sind diese Details echt angenehm, man merkt dass sich jemand Gedanken gemacht hat, wie man sich das Leben angenehmer macht.
Was mir gefällt ist die Clojure Mailingliste und #clojure, dort habe ich oftmals gute Antworten bekommen. Ich frage gerne nach Codereviews und habe da auch schon einiges mitnehmen können. Oftmals ist es einfach irgendeine Funktion, die man nicht kannte, die aber die Aufgabe die man zu lösen versucht elegant löst. Und hier kommen wir etwas zu einem Problem. Clojure hat, womöglich Python-Inspiriert Docstrings und man kann diese mittels ``(doc foobar)`` lesen, aber die dort enthaltene Doku ist oft eher mager oder schwer zu verstehen. Ich habe mir beispielsweise mit ``reduce`` schwergetan, weil ich nicht realisiert habe dass es sich mit Startwert bei einer einelementigen Seq anders verhält (nämlich genau so wie ich brauche) als ohne. Die offizielle Dokumentation ist online allerdings muss ich sagen dass ich ClojureDocs wesentlich besser finde. Was aber auffällt dass es in Clojure-contrib einfach Sachen doppelt gibt (``separate`` ist in ``clojure-contrib.seq`` und ``clojure-contrib.seq-utils``) und einige Sachen schon jetzt deprecated sind. Dabei ist das alles noch nicht so alt... Wo wir gerade bei Dokumentation sind: Full Disclojure ist eine sehr interessante Serie wo in kurzen Episoden Clojure-Features vorgestellt werden, die die etwas "komplexeren" Features beleuchten. Man lernt vielleicht nicht sofort wie man es effizient nutzen kann, aber man weiß zumindest schon mal was es gibt und was man nutzen kann.
Ein weiteres Steckenpferd von Clojure ist Concurrency. Aber ich habe damit recht wenig zu tun, daher kann ich nur sagen dass ich mir das STM-Kapitel in "Programming Clojure" durchgelesen habe, genickt, und dann weitergeblättert habe. Mir kommt es etwas simplistisch vor, aber ohne ein Projekt damit realisiert zu haben ist meine Aussage da wenig Wert. Die Makros habe ich mir nicht angeschaut, aber als Fan von syntax-rules werde ich wohl von dem Common-Lisp artigen System wohl nur mäßig begeistert sein. Die sollen da ein System haben was Variablencapture in Makros verhindern soll, bin mal gespannt. Clojure hat auch Reader-Makros, allerdings sind die fest vorgegeben und damit eher Syntax. Kann man, genauso wie die [] und {} gut oder schlecht finden. Ich finds eher mäßig berauschend, aber es ist keine Katastrophe.
Außerdem hat Clojure wie Scala mit ``sbt`` ein eigenes Build-Tool, ``leiningen`` (nach einer Erzählung namens "Leiningen Versus the Ants" benannt, was ein witziges Wortspiel ist). Es erinnert wie ich finde auch weniger an ``ant`` sondern mehr wie ``maven`` oder eben ``sbt``. Wenn man sich an die Standardstruktur hält dann funktioniert es hinreichend gut (ich hab zwar gleich mal einen Bug gefunden, wurde aber innerhalb kürzester Zeit - während der Weihnachtsfeiertage - gepatcht) und kann auch so Standardsachen wie JARs von Clojars runterladen und Zeug paketieren. Das wirkt auf mich etwas aufgeräumter als ``pip`` und ``virtualenv``, gibt sich aber wohl nicht viel.
Was mir nicht gefallen hat sind die JVM-Startupzeiten. Mein Skript läuft innerhalb kürzester Zeit durch (was für Clojure spricht), aber von der Gesamtzeit macht das Booten der JVM den Großteil aus. Zudem unter amd64 nur die Server-VM verfügbar ist und die braucht mehr Zeit als die Client-VM. Was auch schade ist, ist dass GCJ das nicht kompilieren kann - da ist Common Lisp derzeit was native Compiler angeht besser aufgestellt. Aber vielleicht sollte ich micht als Python-User nicht so anstellen. Ich habe auch Ansätze wie Nailgun probiert, eine Art die JVM persistent laufen zu lassen und da den Code einfach immer nachzuladen, aber wirklich gut funktioniert das nicht. Gerade da hätte ich mir echt Verbesserung gewünscht, denn nachdem ich die Startup-Geschwindigkeit von Python gewohnt bin oder das Hintergrundkompilieren durch ``omake`` fühlt sich Code ausprobieren, Tests laufenlassen etc. recht schwerfällig an. Scala ist da zugegebenermaßen noch schlechter aufgestellt, denn ``scalac`` ist keine Geschwindigkeitsbestie.
Wiederrum positiv finde ich, dass Clojure ein brauchbares vim-Plugin hat, das out-of-the-Box halbwegs gut einrückt und highlightet. Eine Integration wie Slime hat vim nicht, zumindest wenn man nicht Nailgut nutzt. Aber Nailgun funktioniert bei mir nicht überzeugend.
Oh, nun ist es mehr geworden als ich dachte. tl;dr: Clojure gefällt mir entgegen Lisper-Vorurteilen besser als erwartet.