Fundierte Artikel über Technologien, die das Kommende formen.

Warum Lisp nach sechs Jahrzehnten noch immer zählt

Lisp ist über 65 Jahre alt und prägt das Sprachdesign bis heute. Was ihn so langlebig macht und was Entwickler heute davon lernen können.

Uralter Baum, dessen Äste aus verschachtelten Klammern bestehen und leuchtende Früchte tragen

Alle paar Jahre schreibt jemand einen Essay mit dem Titel „Lisp ist tot“, und alle paar Jahre weist jemand anderes darauf hin, dass die Hälfte der Features in der Lieblingssprache von heute aus Lisp stammt. Garbage Collection, First-Class-Functions, Closures, dynamische Typisierung, Homoiconicity, REPL-getriebene Entwicklung, Makros – alles wurde in Lisp erfunden oder populär gemacht, lange bevor die meisten heutigen Entwickler geboren waren. Lisp ist ein Projekt, dessen volle Würdigung Jahrzehnte dauert.

Und doch. Frag einen praktizierenden Entwickler, ob er schon mal mit Lisp gearbeitet hat, und die meisten sagen Nein. Frag, ob er ein Produktivprojekt in Lisp starten würde, und die meisten schauen dich komisch an. Hier gibt es ein Paradox: Lisps Ideen haben die Programmierwelt erobert, Lisp selbst bleibt aber eine Nische. Zu verstehen, warum, ist spannender als die Sprüche á la „Lisp ist gut, andere Sprachen sind schlecht“, die man in den meisten Lisp-Fürsprachen findet.

Was Lisp wirklich richtig gemacht hat

Lisp wurde 1958 von John McCarthy entwickelt. Zur Einordnung: FORTRAN war gerade ein Jahr alt. COBOL gab es noch nicht. Der PDP-1-Minicomputer sollte erst zwei Jahre später ausgeliefert werden. Lisp wurde zuerst auf dem Papier entworfen und dann auf einer IBM 704 implementiert – einer Maschine, die einen ganzen Raum füllte und 36-Bit-Wörter verwendete.

Trotzdem traf McCarthy Designentscheidungen, die sechs Jahrzehnte später noch relevant sind. Die großen davon:

Code als Daten (Homoiconicity)

In den meisten Sprachen sind Code und Daten grundlegend verschiedene Dinge. Man schreibt Code in einer Syntax, und der Code manipuliert Datenstrukturen. In Lisp ist Code selbst Daten. Ein Lisp-Programm besteht aus Listen. Der Ausdruck (+ 1 2) ist gleichzeitig ein Funktionsaufruf, der 1 und 2 addiert, und eine Liste mit drei Elementen: dem Symbol +, der Zahl 1 und der Zahl 2. Du kannst diese Liste manipulieren – umordnen, Elemente hinzufügen, transformieren – und das Ergebnis dann ausführen.

;; This is a function call
(+ 1 2)  ; => 3
;; This is a list containing the same elements
'(+ 1 2)  ; => (+ 1 2)
;; You can build code as data and then evaluate it
(def my-expr '(+ 1 2))
(eval my-expr)  ; => 3
;; Or transform it
(def doubled (list '* 2 my-expr))
;; doubled is now (* 2 (+ 1 2))
(eval doubled)  ; => 6

Das klingt zunächst nach einer Kuriosität, bis man erkennt, was es möglich macht: Programme, die Programme schreiben. Lisp-Makros arbeiten nicht mit reiner Textersetzung wie die C-Präprozessor-Makros. Sie erhalten die eigentliche Programmstruktur als Datenstruktur, transformieren sie mit der vollen Mächtigkeit der Sprache und geben neue Programmstruktur zurück. Das ist Codegenerierung zur Compilezeit, wobei der Compiler selbst als deine API dient.

Das REPL als Entwicklungsumgebung

Lisp hat die Read-Eval-Print-Loop populär gemacht – die Idee, dass man einen Ausdruck eintippen, sofort auswerten lassen und das Ergebnis sehen können sollte. Das klingt in einer Zeit, in der jede Sprache ein REPL hat, nicht besonders spektakulär, aber Lisps REPL geht weiter als die meisten.

In Common Lisp oder Clojure testest du nicht nur isolierte Ausdrücke im REPL. Du entwickelst ganze Systeme interaktiv. Du definierst eine Funktion, testest sie, definierst sie neu – und das alles, während das Programm läuft. Du kannst den laufenden Zustand inspizieren und verändern. Du kannst eine einzelne Funktion neu kompilieren, ohne die Anwendung neu zu starten. Der Entwicklungszyklus ist nicht Schreiben-Kompilieren-Ausführen-Debuggen, sondern ein fortlaufendes Gespräch mit einem lebenden System.

Clojure-Entwickler arbeiten besonders oft so: Sie verbinden ihren Editor mit einem laufenden REPL, schreiben Code im Editor, werten einzelne Ausdrücke per Tastendruck aus und sehen die Ergebnisse sofort. Die Feedbackschleife misst sich in Millisekunden, nicht in Minuten. Hat man einmal so gearbeitet, fühlt sich der klassische Edit-Compile-Restart-Zyklus an wie Kommunikation per Brief.

Minimale Syntax, maximale Flexibilität

Lisps Syntax – oder deren Fehlen – ist das umstrittenste Merkmal der Sprache. Es gibt keine speziellen Formen für if-Anweisungen, for-Schleifen oder Klassendefinitionen. Alles ist eine Liste, deren Operator an erster Stelle steht: (if condition then-expr else-expr), (defn name [args] body), (for [x (range 10)] (* x x)). Die Klammern, die Neulinge abschreckend finden, sind der Preis für die syntaktisch regelmäßigste Sprache, die je entworfen wurde.

Diese Regelmäßigkeit hat einen praktischen Nutzen: Tooling. Wenn jedes Konstrukt dem gleichen Muster (operator operanden...) folgt, können Editoren Code zuverlässig einrücken, refaktorieren und strukturell navigieren. „Umschließenden Ausdruck auswählen“ ist in Lisp eindeutig – es sind immer die passenden Klammern. Bei Sprachen mit vielfältiger Syntax ist strukturelles Editieren ein ungelöstes Problem. In Lisp ist es seit den 1960er-Jahren gelöst.

Warum Lisp nicht gewonnen hat

Wenn Lisp so gut ist, warum nutzt dann nicht jeder es? Die typische Antwort von Lisp-Fans lautet „Die Branche liegt falsch“, was wenig hilfreich und meist nicht zutreffend ist. Lisp hat keine breite Verbreitung erreicht, und dafür gibt es handfeste, nicht-triviale Gründe.

  • Die Ökosystem-Lücke. Für die meisten praktischen Aufgaben zählen Bibliotheken mehr als Sprachfeatures. Python ist nicht beliebt, weil seine Syntax so schön ist – sondern weil pip install dir sofort Zugriff auf Tausende gut gepflegter Pakete für Data Science, Webentwicklung, Machine Learning und alles andere gibt. Lisp-Ökosysteme (Common Lisp, Scheme, Clojure) sind solide, aber kleiner. Du verbringst mehr Zeit damit, Dinge von Grund auf zu schreiben oder schlechter gepflegte Bibliotheken anzupassen.
  • Die Lernkurve liegt am Anfang. Lisps Syntax mit Klammern ist, sobald man sie verinnerlicht hat, denkbar einfach, stellt für Einsteiger aber eine echte Hürde dar. Wichtiger noch: Idiomatisches Lisp zu schreiben erfordert ein Denken, das ungewohnt ist, wenn man aus prozeduralen oder objektorientierten Sprachen kommt. Der Gewinn kommt später, aber viele Entwickler springen ab, bevor sie dort ankommen.
  • Fragmentierung. „Lisp“ ist keine einzelne Sprache, sondern eine Familie. Common Lisp, Scheme, Racket, Clojure, Emacs Lisp, Hy, Janet – jede mit eigenen Stärken, eigenen Ökosystemen und eigenen Communities. Diese Zersplitterung verhindert die Bündelung von Kräften, die Ökosysteme wachsen lässt.
  • Unternehmensrückhalt ist entscheidend. Java hatte Sun. C# hatte Microsoft. Go hat Google. Python hatte Google und eine riesige Data-Science-Community. Die erfolgreichste Lisp-Sprache im Produktiveinsatz, Clojure, hat an Zugkraft gewonnen, teils weil Rich Hickey ein außergewöhnlich klarer Denker und Kommunikator ist – doch es fehlt ihr der institutionelle Schub, der die Massenverbreitung antreibt.

Clojure: Lisp, das aus der Geschichte gelernt hat

Clojure verdient besondere Aufmerksamkeit, weil es zeigt, wie man Lisps Stärken für die moderne Softwareentwicklung praktisch nutzbar macht. Rich Hickey hat Clojure 2007 entworfen – mit klarem Bewusstsein dafür, warum frühere Lisps keine breite Verbreitung fanden, und er hat bewusst Kompromisse getroffen.

  • Läuft auf der JVM. Statt ein eigenes Ökosystem von Grund auf zu bauen, läuft Clojure auf der Java Virtual Machine und kann jede Java-Bibliothek direkt nutzen. Brauchst du einen Datenbanktreiber? Nimm den Java-Treiber. Brauchst du einen HTTP-Client? Nimm den Java-Client. Diese eine Entscheidung verschaffte Clojure vom ersten Tag an Zugriff auf Tausende erprobter Bibliotheken.
  • Standardmäßig unveränderlich. Clojures Kerndatenstrukturen – Listen, Vektoren, Maps, Sets – sind unveränderlich. Du änderst keine Map, sondern erzeugst eine neue mit deinen Änderungen. Das eliminiert ganze Klassen von Nebenläufigkeitsfehlern und macht Programme leichter nachvollziehbar. Die persistenten Datenstrukturen darunter teilen sich Struktur, damit das effizient bleibt.
  • Praktische Nebenläufigkeitsprimitive. Atoms, Refs, Agents – Clojure bietet mehrere Nebenläufigkeitsmodelle, jedes für andere Muster geeignet. Das war keine theoretische Eleganz, sondern beruhte auf Hickeys eigener Erfahrung mit den Schmerzen geteilten, veränderlichen Zustands in produktiven Java-Anwendungen.
  • ClojureScript. Clojure wird nach JavaScript kompiliert und hat damit Zugang zu den Browser- und Node.js-Ökosystemen. Schreib dein Backend in Clojure, dein Frontend in ClojureScript und teile Code zwischen beiden. Das ist dasselbe Versprechen wie bei TypeScript oder Kotlin Multiplatform, nur kam es früher.

Was moderne Sprachen übernommen haben

Selbst wenn du nie eine Zeile Lisp schreibst, nutzt du seine Ideen täglich. Der Einfluss ist so durchdringend, dass ihn nachzuverfolgen eine Übung darin ist zu sehen, wie tief er reicht.

map, filter und reduce in JavaScript sind direkte Nachfahren der Listenverarbeitungsfunktionen aus Lisp – der Name der Sprache steht buchstäblich dafür (LISt Processing). Pythons Listen-Comprehensions sind syntaktischer Zucker für dieselben Operationen. Rusts Pattern Matching lässt sich über ML bis zu Lisps cond-Ausdrücken zurückverfolgen. Swifts Closures, Kotlins Lambda-Syntax, Javas Streams-API – all das sind Lisp-Konzepte im neuen Gewand.

Die Makrosysteme in Rust (macro_rules! und prozedurale Makros), Elixir, Nim und Julia sind direkt von Lisp-Makros inspiriert – allerdings erreicht keines davon ganz dieselbe Nahtlosigkeit, weil keine dieser Sprachen eine homoikonische Syntax hat. Wenn dein Code Daten ist, sind Makros trivial. Hat dein Code eine komplexe Syntax, müssen Makros diese Syntax parsen und erzeugen, was zusätzliche Reibung erzeugt.

REPL-getriebene Entwicklung ist in der Data Science zur Norm geworden (Jupyter-Notebooks sind im Grunde REPLs mit Persistenz), und Werkzeuge wie Figwheel und shadow-cljs haben Hot-Reloading in die Frontend-Entwicklung gebracht – eine Idee, die direkt aus der Lisp-Tradition stammt, gegen ein laufendes System zu entwickeln.

Solltest du Lisp lernen?

Die ehrliche Antwort hängt davon ab, was du davon haben willst.

Wenn du ein besserer Programmierer werden willst, indem du erweiterst, wie du über Code denkst: ja, auf jeden Fall. Lisp zu lernen – insbesondere das Denken in Datentransformationen, rekursiven Strukturen und Code-als-Daten – verändert, wie du Probleme in jeder Sprache angehst. Es ist wie beim Erlernen der funktionalen Programmierung: Auch wenn du Haskell nie produktiv einsetzt, machen dich die Konzepte zu einem besseren Python- und JavaScript-Entwickler.

Wenn du produktive Software mit Lisp bauen willst: Clojure ist die praktische Wahl. Es hat ein echtes Ökosystem, eine professionelle Community und Unternehmen wie Nubank, Walmart oder CircleCI, die es im großen Maßstab einsetzen. Common Lisp ist machbar, aber eine Nische – du verbringst mehr Zeit mit Infrastruktur und weniger mit deinem eigentlichen Problem. Scheme und Racket eignen sich hervorragend für Ausbildung und Sprachforschung, sind aber für universelle Softwareentwicklung weniger praktisch.

Wenn du neugierig bist, dich aber noch nicht festlegen willst, lies etwas Clojure-Code. Keine Tutorials – echten Produktionscode. Schau dir an, wie Clojure-Entwickler kleine Funktionen zusammensetzen, wie sie die Threading-Makros (-> und ->>>) nutzen, um Datenpipelines zu bauen, und wie sie Domänen mit einfachen Maps statt mit Klassen modellieren. Du wirst einen Programmierstil sehen, der knapper, kombinierbarer und stärker auf Datenfluss ausgerichtet ist als das, was du in gängigen OOP-Codebasen findest.

Lisps Langlebigkeit ist keine Nostalgie. Sie ist das Ergebnis davon, dass ein paar grundlegende Dinge so richtig gemacht wurden, dass sechs Jahrzehnte Sprachdesign sie nicht verbessern konnten. Die Klammern sind gewöhnungsbedürftig. Das Ökosystem ist kleiner, als man sich wünschen würde. Aber die Ideen sind zeitlos, und wenn du dich mit ihnen beschäftigst, wirst du in der Sprache, die du täglich nutzt, besseren Code schreiben.