Ich habe mich schon häufig in der Situation wiedergefunden, einem an funktionaler Programmierung interessierten Gesprächspartner zu erklären, daß gerade die Mutter aller funktionalen Sprachen, Lisp, selbst gar nicht viel mit funktionaler Programmierung zu tun hat. Auf die dann meistens folgende, leicht irritierte Frage, was es denn für eine Sprache sei, wenn nicht eine funktionale, habe ich bislang immer verhalten „irgendwas zwischen objektorientiert und funktional“ geantwortet — eine etwas unzufriedenstellende Antwort und vor allen Dingen nichts, womit man wirklich etwas anfangen könnte, wenn man die Sprache noch nicht kennt.

Vor einer Weile habe ich an der Uni eine Vorlesungsstunde über „zirkuläre Programme“ im Rahmen der Vorlesung „Funktionale Programmierung“ gehört — oder besser: ich habe das Skript dazu gelesen, weil jene Vorlesungsstunde an einen unpassenden Termin verschoben worden war (ironischerweise eine Überschneidung mit Funktionentheorie, welche später noch einmal einen Auftritt in diesem Artikel haben wird). Ich blätterte also so dahin, und als ich auf das erste Beispiel stieß, ließ es mich stutzen. Es ging um die Definition einer Datenstruktur durch eine Art Verknotung des Codes — und nur darum. Zugegeben, ich hätte nicht gewußt, was unter einem „zirkülären Programm“ zu verstehen hätte sein können, aber irgendetwas sagte mir, daß das doch keines sein konnte.

Seitdem ist mir klarer geworden, worum es eigentlich geht.

Die Lisp-Denkweise: Ich schreibe die Funktionen, der Rest ergibt sich so nebenbei

Wenn ein Lisper ein Programm schreibt, dann baut er es häufig erst einmal von unten her auf. Er erweitert dabei die Sprache so lange, bis er seine Probleme adäquat darin ausdrücken kann. Diese Spracherweiterungen bestehen primär aus Funktionen und Makros. Datenstrukturen definiert er sich dann, wenn sie sich als nützlich oder notwendig herausstellen.

Ein Lisper programmiert also funktionsorientiert. Er schreibt Makros und Funktionen. Sie bilden die Bausteine seines Programms, und sie liegen im Zentrum seines Denkens. Deshalb interessiert er sich auch nur am Rande für solche Dinge wie Musterangleich (pattern matching) und algebraische Datentypen — einfache structs tun es meist auch. Ein statisches Typensystem würde den Lisper nur bei seiner Arbeit stören, die ja schließlich darin besteht, erst einmal viel Allgemeines, Grundlegendes zu schreiben, das sowieso mit allen oder zahlreichen nicht unbedingt viel gemeinsam habenden Datentypen gleichermaßen zu verwenden ist — Spracherweiterungen eben.

Die funktionale Denkweise: Datentypen und Kinder zuerst!

Der funktionale Programmierer sieht die Welt ganz anders. Für ihn stehen die Datenstrukturen im Mittelpunkt des Interesses. Was ist schon ein Programm ohne Datenstrukturen? Erst einmal definieren wir... natürlich, einen algebraischen Datentypen. Der erfüllt vielleicht irgendwelche Gesetze, die man schön in einer Typklasse ausdrücken kann. Wir fangen also an, Code zu schreiben, der unsere Datenstrukturen transformiert. Dabei befinden wir uns auf einer hohen Abstraktionsebene und haben ständig vor Augen, auf welcher Art von Daten wir gerade arbeiten.

Da die Datenstrukturen so wichtig sind, ist auch statische Typprüfung von ungeheurem Wert und findet in unserem Programm fast alle Fehler, die wir überhaupt machen können. Wir schreiben den Code so allgemein wie möglich, aber wir schreiben keine Spracherweiterungen, denn die würden den Typprüfer verwirren, und überhaupt — wenn wir alles in Datenstrukturen ausdrücken, wieso sollten wir uns dann die Mühe machen, die Sprache für Code zu erweitern? Soviel Code schreiben wir ja gar nicht. Außerdem ist die Syntax der Sprache für die Arbeit mit algebraischen Datentypen optimiert und eignet sich nicht besonders für Erweiterungen.

Auch pattern matching ist ein wesentliches Merkmal von funktionalen Sprachen. Wie sollte es anders sein, wenn man ständig Datenstrukturen destrukturiert und wieder zusammensetzt? Ohne pattern matching könnte man gar nicht leben.

Aber warum heißt diese Programmierweise eigentlich gleich nochmal funktional?

Funktionale Programmierung ist algebraische Programmierung

Wenn man sich die obigen (zweifelsohne stereotypen) Beschreibungen durch den Kopf gehen läßt, fällt einem früher oder später vielleicht auf, daß der Begriff „Funktion“ im Abschnitt über die funktionale Programmierung nirgends vorkommt. In der Tat erscheinen Funktionen als solche in der funktionalen Programmierung eigentlich nebensächlich. Worum es geht, sind die Datenstrukturen, üblicherweise algebraische Datentypen. Diese werden erzeugt, transformiert und konsumiert. Das ist der primäre Sinn und Zweck eines Großteils des Codes eines üblichen funktionalen Programms. Daß dafür gern Funktionen verwendet werden, liegt nur daran, daß sie dafür nützlich sind. Die algebraischen Datenstrukturen sind es, die eigentlich im Mittelpunkt des Geschehens stehen.

In Lisp hat man hingegen in erster Linie die Funktionen und Makros im Kopf, die man geschrieben hat oder noch schreiben möchte. Datenstrukturen sind nur ein nützliches Beiwerk, nichts weiter, zuweilen sogar eher ein notwendiges Übel. Die Funktionalität der einzelnen Codeteile ist es, was in der Hauptsache zählt.

Es gab eine Zeit, in dem man mit dem Begriff „funktional“ die Lisp-Sichtweise meinte. In der Tat: Letztlich arbeitet man in Lisp viel mehr funktionenorientiert als in „funktionalen“ Sprachen im heutigen Sinne. Die Zeiten haben sich geändert. Heutzutage bezeichnet man als funktionale Programmierung eine Programmierweise, die eigentlich algebraische Programmierung heißen sollte.

Bleibt die Frage, wie man die Lisp-Sicht der Welt nennen soll. „Irgendwas zwischen objektorientiert und funktional“ ist vielleicht nicht ganz falsch, aber auch nicht ganz richtig. Sprachorientierte Programmierung vielleicht?

Nebenbei bemerkt: In der Mathematik gibt es ein Gebiet namens Funktionentheorie. Wider jegliche Erwartung geht es darin nicht um Funktionen im allgemeinen Sinne, nicht um Funktionen als Objekte oder Strukturen, sondern schlicht um komplexe Analysis. Der Begriff der Funktion hat offenbar viele Gesichter.