The Joy of Clojure
Einleitung
Wir haben noch keine starke Psychologie und Soziologie der Programmiersprachen. Eines Tages werden wir sie haben, und dann werden wir im Nachhinein verstehen, warum Clojure so mächtig ist.
Abbildung 1: Die Seele! Ein Computer. Solarpunk-Clojure. Das Innere eines Computers. Edelsteine in Zauberwürfelfarben fliegen herum. Licht.
Paul Graham hat den Geschmackstest vorgeschlagen, um die Mächtigkeit einer Sprache zu verstehen. Eine Programmiersprache ist ein Werkzeug zum Denken. Letztlich kommt ihre Kraft daher, dass sie einen Gedanken denken und ausdrücken lässt. Mit Feingefühl (Überempfindlichkeit) merkt man, welche Sprache sich einschränkender anfühlt als eine andere.
Wären alle Sprachen gleich, würden wir Assembler programmieren. Tun wir aber nicht, denn wir leben in einer Welt, in der Sprachen unterschiedlich mächtig sind.
Hier versuche ich, ein paar Dinge zu beschreiben, die Clojure besonders machen, sogar unter den Lisps.
Was Interaktivität bringt
Mit Lisp kann man jedes Stück Code testen, während man es schreibt, und weiß, dass es funktioniert, bevor man zum nächsten Stück geht. Man muss nicht alles, was schiefgehen könnte, gleichzeitig im Kopf behalten. Kleine Experimente sind billig, also macht man viele davon.
Ein einfaches Beispiel:
(file-exists-p "~/.clojure/")
Stimmt der Dateipfad?
Wenn ich Lisp schreibe, kenne ich die Antwort, während ich das Programm schreibe.
Ich eval-e einfach die Form, die den Pfad baut. Dank strukturellem Editieren kann ich
jede Form schnell in (file-exists-p) einwickeln und gleich an Ort und Stelle experimentieren.
Kleine Experimente sind schnell genug, um mit dem eigenen Denken zu verschmelzen. Ich habe die Freiheit und Wendigkeit, einfach zu machen. Dieselbe Kraft bekommen wir im Kleinen beim Entwickeln und im Großen, wenn wir ein Problem in Produktion untersuchen. 1
Ein etwas größeres Beispiel. Ein HTTP-Handler gibt das Falsche zurück. In einer Clojure-REPL füge ich keine Print-Statements ein und starte neu. Ich fange die Anfrage ein:
(defn handler [req] (def req req) ;; inline def: die letzte Anfrage aufheben (process req))
Dann rufe ich den Endpunkt einmal aus dem Browser auf, und ab jetzt ist req eine ganz normale Map in meiner REPL.
Ich kann (process req) so oft aufrufen, wie ich will, process ändern, neu evaluieren, wieder aufrufen.
Der Bug ist jetzt eine Datenstruktur auf meinem Schreibtisch, kein Gespenst im Server.
Philosophie der Einfachheit
Bei mir hat es mit Clojure über die Vorträge von Rich Hickey Klick gemacht. Halte die Dinge einfach. Lass Daten Daten sein. Dann kann man einfach den Kern der Sache bearbeiten und ist fertig. 2 , 3
Er begründet, warum es Clojure gibt, und vertritt Meinungen dazu, wie man Dinge baut. Und das durchzieht die ganze Kultur.
Die Verbindung von Datenorientierung und funktionaler Programmierung, mit pragmatischen Abwägungen für die echte Welt, führt zu einem vollständigen4, robusten Stil ohne Bullshit.
Starke Community
Wenn ich eine Bibliothek benutze, kann ich in den Slack-Channel gehen, und meistens helfen mir die Maintainer. Mehr als einmal habe ich einen Bug gemeldet, und jemand hat ihn innerhalb weniger Tage behoben. Alle sind begeistert von Technik und wollen helfen. Das gehört auch zur Freude an Clojure. Man bekommt glückliche, freundliche Leute. Das ist Kraft. Menschen sind wirksamer, wenn sie glücklich und freundlich sind.
Ganz zu schweigen von Borkdude, der clj-kondo, babashka, nbb und mehr pflegt, und mit dem ich an einigen davon arbeiten durfte.
Versionierung ohne Brüche
Es ist ein ausdrücklicher Wert (wieder aus Rich Hickeys Philosophie), keine Breaking Changes zu machen.5 Das ist eines der Dinge, die man, wenn man sie einmal hat, nie wieder hergeben will.
Wenn man diesen Geschmack einmal hat, denkt man bei einem kaputten Versionssprung: Warum tust du mir das an?
Die Ästhetik der Datenorientierung
Die Core-Bibliothek ist ein riesiger Hebel, weil wir alles, was geht,
in den Raum der Datenstrukturen heben und dann diese Datenstrukturen bearbeiten.
Konfiguration, REST-Antworten, Datenbankabfragen und ihre Ergebnisse: Sobald es in unserem System ist, ist alles dieselbe Art von Zeug.
;; eine Konfigurationsdatei, eine API-Antwort, ein Datenbankergebnis: alles nur Maps (->> users (filter :active) (group-by :team) (update-vals count))
Dieselben vier Funktionen funktionieren, egal ob users aus EDN, JSON oder Datomic kommt.
Ich denke daran als einen Hebel nach dem Muster anheben und bearbeiten. Unix hat das mit Text als Ein- und Ausgabe von Programmen. Sobald man im Reich der Textein- und -ausgabe ist, kann man Shell-Befehle zum Bearbeiten nutzen. Emacs hat dasselbe mit Text in Buffern. Wenn man seine Domäne als Text im Buffer ausdrücken kann, kann man sie dem Nutzer zum Bearbeiten geben und die Core-Bibliothek für Text in Buffern nutzen.
Diese Datenorientierung kann ich von Bibliotheken und dem Code anderer Leute erwarten. Der überwiegende Teil wird immer reine Funktionen auf einfachen Datenstrukturen sein.
Freiheit und Freude
Der Workflow ist eine pure Freude, mit der REPL im Zentrum. Ich starte ein Programm einmal, und es wächst mit meinen Ideen. Ein Programm, das tagelang läuft, ohne dass man es neu startet, macht etwas mit dem Kopf, das ich nicht erklären kann. Die nächste Analogie ist eine Art Freundschaft mit dem Programm.
- Mit
inline defein paar Daten festhalten, die Code-Formen ausarbeiten. - Funktionale Programmierung (und The Value of Values)
- Daten herstellen können
- die Zeit richtig hinbekommen, mit atomaren Abfolgen
- leicht Aufzeichnungen über die Vergangenheit behalten
Ich bekomme eine echte Art von Sicherheit. Die Art, bei der ich nachts schlafen kann, weil ich sehe, dass mein Programm tut, was es soll. Die kommt daher, dass es wenige genug Ideen im Programm gibt, um die relevanten Teile im Kopf zu behalten.
Ein Programm, das klein genug ist, um selbstevident zu sein, funktioniert mit dramatisch höherer Wahrscheinlichkeit. 6
Es hilft, dass alles aus Emacs heraus erreichbar ist, dem anderen uralten und mächtigen Werkzeug, das sich wie eine Verlängerung meines Geistes anfühlt.
Durch diesen Fokus, diese Solidität und Leichtigkeit bin ich beim Programmieren mit Clojure fröhlicher und kreativer.
Ich kann mich auf die eigentlichen Probleme konzentrieren. Also habe ich Zeit, zum Beispiel die User Experience richtig hinzubekommen.
Drei Jahre später
Diesen Post habe ich Anfang 2023 geschrieben, bevor ich beruflich Clojure geschrieben habe. Jetzt tue ich das, an einer großen Clojure- und ClojureScript-Codebasis mit Datomic. Was hat gehalten?
- Die REPL skaliert. In einem großen System zählt die REPL mehr, nicht weniger. Die laufende App ist das, womit ich rede: einen Namespace neu laden, eine Funktion mit echten Daten aufrufen, das Ergebnis anschauen.
- Daten an den Grenzen. Malli-Schemas sind einfach Daten, also kann ein Schema in einer
.cljc-Datei liegen und dieselben Formen im Browser und auf dem Server prüfen. - Anhäufen statt überschreiben. Datomic erfasst Fakten und überschreibt sie nie. Buchhaltung, wie es sein soll.
- Die REPL wird geteilt. Mein Coding-Agent evaluiert Formen in derselben laufenden REPL wie ich. Er rät nicht, was eine Funktion zurückgibt; er fragt das Programm. Interaktivität ist genau das, was ein LLM auch braucht.
Was ich heute ergänzen würde: Die Freude ist nicht nur ästhetisch. Sie ist Geschwindigkeit. Kleine Experimente, viele davon, den ganzen Tag.
Offene Fragen:
- Lässt sich die "Freundschaft mit dem Programm" lehren, oder nur erleben?
- Wird die REPL mit einem Agenten in der Schleife zur Hauptschnittstelle zum Programm, mehr als die Quelldateien?
Zusammenfassung
Hier ist also ein Werkzeug, das mich die richtigen Gedanken denken und ausdrücken lässt. Es lässt mich mühelos dahin kommen, wo ich hinwill.
Von Lisp bekomme ich die Interaktivität und die Tradition von FP und Einfachheit. Wenn ich ein kleines Experiment mit dem Code machen will, kann ich einfach ein kleines Experiment mit dem Code machen.
Dazu kommen eine solide, verlässliche Community, die Datenorientierung und der Hebel großer Plattformen.
Ich kann Informationssysteme modellieren und das Programm genau das tun lassen, was es tun muss, und nichts mehr.
Clojure liefert einfach.
The silent competence of the computer.
(Marvin Minsky)
Anmerkung
Es gibt ein Buch mit demselben Titel, von Michael Fogus und Chris Houser. Das ist kein Zufall. Die Kraft von Clojure liegt auch in der Freude, die es bringt. Ein anderer Titel für diesen Post könnte Freude ist Kraft sein, in Anlehnung an Paul Grahams Succinctness is Power.
Mehr zu Clojure: Clojure und interaktives Programmieren.
Fußnoten
Fußnoten:
Bradford Cross, How startups can move fast with Clojure, ein sehr überzeugender Vortrag.
Ich glaube, Spuren desselben Handwerks des Erledigens auch anderswo zu sehen: Brian Will, Object-Oriented Programming is Embarrassing, dessen Gegenbeispiele sehr geradlinig und vollständig sind, und Chris Keathley, SOLID code isn't flexible.
Rich Hickeys größte Hits. Der Einstiegspunkt. Lass dir den Kopf durch Einfachheit und klares Denken erweitern.
Vollständig in dem Sinn, dass man ein Stück Code macht, das die Aufgabe erledigt, statt Logik überall zu verstreuen.
Rich Hickey, Spec-ulation, Clojure/conj 2016.
Generiert von Claude im Auftrag von Benjamin Schwerdtner.
Stuart Halloway hat ähnliche Gedanken, Radical Simplicity. Auch andere, etwa Dan Friedman, haben das recht ausdrücklich gemacht. Ich denke, inzwischen ist offensichtlich, dass Einfachheit entscheidend ist, um gute Software zu bauen. Die Frage ist: Was ist Einfachheit, und wie macht man sie?