Clojure und interaktives Programmieren
Clojure ist ein Lisp, die einzige Sprache, die auch schön ist. Auf der JVM zu Hause, damit sie überall hinkommt. Funktional, ausdrucksstark, mit klaren Meinungen.
Ich schreibe seit fünf Jahren Clojure, inzwischen beruflich bei United Signals an Flowniq: ClojureScript im Browser, Clojure und Datomic auf dem Server, Malli-Schemas, die beide Seiten teilen. Diese Seite sammelt, was ich über Clojure, Lisp und interaktives Programmieren denke und schreibe.
Zum Einstieg
- Keine Iterationen: die Lisp-REPL für alle, die noch nie eine benutzt haben.
- The Joy Of Clojure: was Clojure besonders macht, sogar unter den Lisps.
- Lisp: was es heißt, dass eine Sprache schön ist.
- So Many Parenthesis!: Chris Nuernbergers kurze Einführung in Clojure.
Interaktives Programmieren
Das magische Stück an Lisp: Man baut das Programm, während es läuft. Man schreibt keine Datei, kompiliert, startet und liest den Absturz. Man schickt eine Form an das laufende Programm, schaut sich die Antwort an und ändert die nächste Form. Kleine Experimente werden so billig, dass sie mit dem eigenen Denken verschmelzen. Es ist ein Gespräch mit dem Programm.
Posts zum Handwerk:
- Small and Friendly Errors with CIDER und Zappy CIDER Eval Overlays: die Feedback-Schleife in Emacs enger machen.
- Meta The Meta: eine JVM-REPL, um babashka-Skripte zu entwickeln.
- Emacs, Clojure, Lispy: a Mind Meld (Video).
- Jacking in nbb und bb Tasks and More (Video).
Was ich mit Clojure gebaut habe
- A Vector-Symbolic Clojure: ein Clojure, in dem jeder Wert ein einziger hochdimensionaler Vektor ist.
- Acht Damen in core.logic: relationales Programmieren, der Solver übernimmt die Suche.
- shadow-css for Personal Use: CSS in ClojureScript, Daten bis ganz nach unten.
- Binaural Beats with Scittle und Scittle Prints Itself: ClojureScript ohne Build-Schritt.
- Bobscheme: ein Lisp-Dialekt, dessen Code JSON ist.
- Which Clojure Core Function Are You?: ein Quiz, zum Spaß.
- Open Source: Beiträge zu clj-kondo, nbb, babashka nREPL und CIDER (siehe meinen Lebenslauf).
Datenorientierung
Clojures Stil ist das, was Yehonathan Sharvit datenorientiertes Programmieren nennt: Code und Daten trennen, Daten mit generischen Datenstrukturen darstellen, Daten unveränderlich halten.1 Nicht zu verwechseln mit dem gleichnamigen Ansatz aus der Spieleentwicklung, dort geht es um das Speicherlayout.
Datenorientierung nutzt allgemeine Datenkonstrukte und Funktionen, etwa JSON-artige Daten und reine Funktionen, um eine Domäne der Informationsverarbeitung darzustellen.
Um eine Bibliothek zu modellieren, modellieren wir nicht Bücher und Bibliothekare, sondern das Bestandsbuch, das ein Bibliothekar benutzen würde. Wir modellieren nicht Studenten und Kurse, sondern das Kursregister.
Gute Datenorientierung nimmt die Tradition der Buchhaltung ernst. Eine erfasste Tatsache ist eine Tatsache. Sie wird nicht nachträglich geändert; man kann nur eine neue Tatsache erfassen, eine Korrektur. (Genau so funktioniert Datomic, und deshalb fühlt es sich aus Clojure heraus so natürlich an.)
Studenten und Kurse als Objekte zu modellieren, ist gut für eine Simulation, wenn das Programm tatsächlich das Verhalten von Studenten abbilden soll. Das passt vielleicht in ein Spiel, aber nicht in ein Bibliothekssystem. Simula wurde für Simulationen gebaut, und Simula 67 gilt als die erste objektorientierte Sprache.2 Objektorientierung wurde geboren, um Dinge zu simulieren. Informationssysteme sind keine Simulationen.
Clojure ist seltsam
- Es ist ein Lisp, und Lisp ist ein Aufräumen der Informatik. Alan Kay nannte den halbseitigen Lisp-Evaluator im LISP-1.5-Handbuch "die Maxwell-Gleichungen der Software".3 Lisp gibt es, weil es näher an der Wahrheit dessen ist, worum es beim Programmieren geht. Um Lisps Paradigma zu verbessern, müsste man echte Entdeckungen über die Grundlagen des Programmierens machen. Und die würden uns nicht sagen, dass es etwas Besseres als Lisp gibt, sondern mehr über die wahre Natur von Lisp.
- Pragmatismus ist Teil seiner Designphilosophie.
- Es hat eine Tradition von Handwerk, zu der Designarbeit gehört. Keine Designarbeit zu machen, ist schlechtes Clojure-Programmieren.
Clojure 2 ist ein inkohärentes Konzept
Ein "Python 4" oder ein "Go 2" ist eine kohärente Idee: Man sammelt Feedback, entwirft und implementiert, macht es besser, nennt es Version n+1 und liefert es aus.
Für Clojure wäre das seltsam, denn Pragmatismus ist ein Kernwert der Sprache.
- Clojure ist auf Dauer gebaut. Veränderung passiert durch Wachstum: mehr anbieten, weniger verlangen, und wenn wirklich etwas brechen muss, bekommt es einen neuen Namen.4
- Eine neue Sprache zu bauen, kostet Zeit und Ressourcen, und die werden gegen die Abwägungen gewogen.
- Pragmatismus heißt, dass nichts tun auf dem Tisch liegt, wie die Nullhypothese in der Wissenschaft. Man muss fragen: Ist es besser, die Sache zu tun, als nichts zu tun?
Kein Naturgesetz verhindert also Clojure 2, aber es gibt das Nächstbeste: eine (rationale) memetische Entscheidungsstruktur.
Mit dem richtigen Wissen d, nämlich Clojures Designphilosophie, wird eine rationale Person das Verhalten X
in der Menge vorhersehbarer Situationen E (einer Umgebung) nicht an den Tag legen.
Eine mögliche Situation außerhalb von E: Jemand hat einfach Spaß daran, Clojure 2 zu bauen.
Aber pragmatische Clojure-Programmierer haben jede Menge Spaß daran, Clojure kreativ für andere Probleme zu nutzen.
Die pragmatische Designphilosophie, ein Mem, bringt das Verhalten von Clojure-Programmierern durch Designarbeit näher an die Wirklichkeit, eine Form von Fehlerkorrektur. Ressourcen und Abwägungen sind real. Wenn Fehlerkorrektur uns näher an die Wirklichkeit bringt, landen wir bei derselben Wirklichkeit. Sonst ist es schlechte Fehlerkorrektur, oder schlechte Designarbeit.
Große Geister denken gleich, weil sie Fehler korrigieren. Und gute Clojure-Programmierer verhalten sich gleich. Clojures Pragmatismus ist analog zur Rationalität: Beides sind Kulturen der Fehlerkorrektur, Kulturen von Vermutung und Kritik [Popper, Deutsch].
Clojure um seiner selbst willen zu benutzen, wäre schlechtes Clojure-Programmieren
Manchmal sprechen die Abwägungen für eine andere Sprache, um eine Aufgabe zu erledigen. Ein Clojure-Programmierer sagt das als Erster.
Zwingende Programmierkonstrukte
Statische Typen
Sagt eine Romanautorin: "Alle Sätze müssen zwischen 5 und 15 Wörter lang sein"?
Das Programm ist die Geschichte; der Quellcode ist der Text. Regeln über die Form des Textes machen die Geschichte nicht besser.
Puzzle-orientiertes Programmieren hat seinen Platz, vielleicht in der Lehre. Beim Lösen echter Probleme sollte es eine Nebenrolle spielen. Clojures Antwort: Daten dort beschreiben, wo es zählt, an den Grenzen, mit Schemas (spec, Malli), die selbst Daten sind.
Konkretionen
- Information in maßgeschneiderte Klassen verpacken, eine
Person, einProduct, wo einfache Daten genügen würden. Rich Hickey nennt das Daten-Konkretionen, nicht Daten-Abstraktionen.5 - Das mächtige Werkzeug der Mini-Sprachen benutzen, um Geschäftslogik auszudrücken, wo eine Map gereicht hätte.
Offene Fragen:
- Wo verläuft die Grenze zwischen einer guten Mini-Sprache und einer Konkretion? Datalog ist eine Mini-Sprache, und eine gute.
- Machen LLMs, die einfache Daten mühelos lesen und schreiben, Datenorientierung wertvoller oder weniger wertvoll?
Fußnoten:
Yehonathan Sharvit, Data-Oriented Programming (Manning, 2022). Sharvit stellt die Prinzipien als sprachunabhängig dar; Clojure verkörpert sie.
Dahl und Nygaard. Simula I (1962–65) war die Simulationssprache; Simula 67 brachte Klassen, Objekte, Vererbung und virtuelle Prozeduren.
A Conversation with Alan Kay, ACM Queue, 2004. Gemeint ist der Evaluator auf Seite 13 des LISP 1.5 Programmer's Manual.
Rich Hickey, Spec-ulation, Clojure/conj 2016: Software durch Hinzufügen und Lockern wachsen lassen, nie durch Brüche.
Rich Hickey, Effective Programs, Clojure/conj 2017 (Transkript).
Generiert von Claude im Auftrag von Benjamin Schwerdtner.