Lisp — ein Wegweiser für Einsteiger


A God's Lament

Some said the world should be in Perl;
Some said in Lisp.
Now, having given both a whirl,
I held with those who favored Perl.
But I fear we passed to men
A disappointing founding myth,
And should we write it all again,
I'd end it with
A close-paren.

— Randall Munroe, XKCD #312


Inhaltsverzeichnis

Einführung
Ist Lisp denn eigentlich noch interessant?
Was macht Lisp so besonders?
Scheme und Lisp
Ist Lisp eine funktionale Programmiersprache?
Wie stark unterscheiden sich Scheme und Common Lisp wirklich?
Wo soll ich mich umsehen, wenn ich mich für Scheme interessiere?
newLISP hört sich auch nach einer Alternative an. Ist es eine?
Ressourcen für den Einstieg
Die Erstinstallation einer Lispumgebung
Welche Common-Lisp-Implementierung ist die richtige für mich?
Tutorien und Bücher
Nachschlagewerke
LOOP
Unterstützung durch Gleichgesinnte und Experten
Bibliotheken für GUIs, Sockets et cetera
Klassische Stolpersteine
Ich verstehe das Paketsystem nicht ganz...
Das Fremde kennenlernen
Warum die klammerlastige Syntax?
Ist die große Freiheit in Lisp nicht schädlich für Teams?
Warum die anachronistischen Namen?
Wie erstelle ich ein ausführbares Programm (z.B. eine .exe-Datei)?
Wird Lisp irgendwo praktisch angewandt?
Häufige Mißverständnisse
Ist »CLISP« eine Abkürzung für Common Lisp?
Ist Lisp langsam?

Einführung

Dieses Dokument soll einige oft von interessierten Neulingen gestellte Fragen beantworten, weit verbreitete Mißverständnisse ausräumen und als Einstiegspunkt für Lernwillige dienen.

Es ist vermutlich nicht vollkommen objektiv und wertungsfrei, sondern eine in mancherlei Hinsicht wertende Zusammenstellung von Ratschlägen, die der Autor intuitiv geben würde, wenn er jemandem gegenüberstünde, der ein paar Fragen zu Lisp hat und wissen möchte, wo und wie er anfangen soll, mehr über diese den meisten kaum bekannte Sprachfamilie zu lernen.

Wer sich gar noch nie an der Computerprogrammierung versucht hat, sollte wahrscheinlich gleich im Abschnitt »Ressourcen für den Einstieg« anfangen. Alle anderen möchte ich bitten, noch ein bißchen Geduld mit mir zu haben und zumindest den Abschnitt »Ist Lisp denn eigentlich noch interessant?« zu lesen. Die restlichen Kapitel kann man dann gemäß dem eigenen Ermessen und Interesse behandeln.

Faktische Korrekturen und Verbesserungsvorschläge sind jederzeit willkommen.

Ist Lisp denn eigentlich noch interessant?

Please don't assume Lisp is only useful for Animation and Graphics, AI, Bioinformatics, B2B and E-Commerce, Data Mining, EDA/Semiconductor applications, Expert Systems, Finance, Intelligent Agents, Knowledge Management, Mechanical CAD, Modeling and Simulation, Natural Language, Optimization, Research, Risk Analysis, Scheduling, Telecom, and Web Authoring just because these are the only things they happened to list. — Kent Pitman

Wer den Namen »Lisp« hört, denkt vielleicht, soweit er sich überhaupt etwas darunter vorstellen kann, an eine antiquierte Sprache, die höchstens von historischem Interesse ist. Wer schon etwas weiter recherchiert hat, hat vielleicht von den beiden heutzutage relevanten Dialekten Common Lisp und Scheme erfahren und den Eindruck erhalten, daß einerseits Scheme eine unter Umständen ästhetische, aber auf jeden Fall akademische und praxisuntaugliche und Common Lisp eine barocke, veraltete und übermäßig komplexe Sprache sind, deren Innovationen womöglich vor zwanzig Jahren von Interesse gewesen sein mögen, aber heute ohnehin überall (z.B. in Java, Python oder Ruby) bereits unterstützt werden.

Diese typischen Eindrücke sind nicht ganz falsch und nicht ganz richtig: Scheme ist, jedenfalls wenn man damit nur Standard-Scheme bezeichnet, tatsächlich vor allem eine Lehr- und Forschungssprache, die ohne große Zusätze kaum produktiv verwendbar ist. Auch stimmt es, daß Common Lisp groß ist und viele der ehemals herausragenden Merkmale inzwischen in anderen Programmiersprachen Einzug gefunden haben. Nichtsdestotrotz bieten aber beide Sprachen nach wie vor Funktionen, die sonst nirgends in solch klarer und konsequenter Form anzutreffen sind und deren Erlernen den Horizont des Lernenden ganz enorm erweitern können — und gerade Letzteres sollte einem guten Informatiker eigentlich unbedingt erstrebenswert erscheinen. Darüberhinaus ist es mit der praktischen Nichtanwendbarkeit von Lisp (vor allem Common Lisp) gar nicht so weit her, wie man denkt. Doch dazu später mehr.

Zuerst möchte ich nämlich eine Klarstellung loswerden: Common Lisp und Scheme sehen einander zwar sehr ähnlich, aber es handelt sich trotzdem um grundverschiedene Sprachen. Wer bereits Scheme gelernt hat (z.B. nebenbei während einer Einführung in die Informatik), sollte Common Lisp nicht gleich mit einem Händewinken verwerfen, weil es auf den ersten Blick wie ein häßlicherer Bruder von Scheme aussieht. Der Schein trügt: Kaum jemand schreibt z.B. in Common Lisp seine Schleifen in rekursivem Stil, und handfeste objektorientierte Programmierung ist an der Tagesordnung. Auch das sind aber nur zwei eher schwache und oberflächliche Beispiele für die Unterschiede zwischen den beiden Sprachen, und es gibt auch für Scheme-Kenner in Common Lisp noch einiges zu entdecken.

Dieser Wegweiser dreht sich primär um Common Lisp, nicht um Scheme. Bis jetzt hat der Begriff »Lisp« noch beide Sprachen umfaßt, im folgenden bezieht er sich aber grundsätzlich auf Common Lisp. Wo Informationen über Scheme hineinpassen, werden solche natürlich trotzdem erwähnt.

Was macht Lisp so besonders?

[Der Student] kann sich so völlig dem hingeben, was er naiv für die Computerwissenschaft hält, also der bloßen Verfeinerung seiner Programmiertechniken, daß er sich auf diese Weise effektiv daran hindert, etwas wirklich Wesentliches zu studieren. — Joseph Weizenbaum, Die Macht der Computer und die Ohnmacht der Vernunft

Die Interessantheit von Scheme rührt vor allem daher, daß die Sprache sehr klein und konsequent ist, und daß Funktionen wie alle anderen Objekte behandelt werden. Common Lisp ist hingegen eine Sprache, die nicht nur ein einziges Konzept verkörpert, sondern die sich vielmehr durch ihre integrierte Vielfalt auszeichnet und eine Menge Konzepte enthält, die selbst einem erfahrenen Programmierer erst einmal völlig fremd vorkommen können, obwohl sie zum Teil einen kaum anders zu erreichenden praktischen Wert haben. Als Beispiele seien genannt:

Viele sind der Ansicht, daß Makros das entscheidende Unterscheidungsmerkmal von Lisp-artigen Sprachen überhaupt sind. Andere sehen eher die unvergleichliche Interaktivität der Programmierung in Lisp als den großen Vorteil. Die Meinungen gehen hier also auseinander.

Dieser fehlende Konsens ist auch der wahrscheinlichste Grund dafür, daß sich die Entwicklung in verschiedenen Lispumgebungen, vor allem aber zwischen Scheme und Common Lisp, so unterschiedlich anfühlt. Die meisten Scheme-Implementierungen legen auf Interaktivität keinen so großen Wert wie die meisten Common-Lisp-Umgebungen.

Wie dem auch sei: Keinen Zweifel gibt es jedenfalls daran, daß die Lispgemeinde ihre ganz eigenen Traditionen und Denkweisen hat, und daß die Mitgliedschaft einer Sprache in der Familie der Lisps keine rein technische, sondern auch eine kulturelle Frage ist, die sich zu einem gewissen Grade in letzter Instanz nur gefühlsmäßig beantworten läßt.

Historische Notiz: Während heute Scheme und Common Lisp gemeinhim als die beiden wichtigen Lisps bezeichnet werden — was auch der Ansicht des Großteils der Scheme- und CL-Gemeinden entspricht — besteht bei beiden kein absoluter Konsens, daß sie eigentlich zur Lispfamilie gehören. Vor ungefähr zwanzig Jahren (d.h. in den späten 80ern) war es noch ziemlich kontrovers, ob man das sich damals noch in der Entwicklung befindende Common Lisp noch als ein Lisp anerkennen sollte, weil es mit vielen Traditionen radikal brach. Vor allem Interlisp-Anhänger sahen in Common Lisp den Versuch, den Geist von Lisp in seinem eigenen Namen zu untergraben und die wahre Lisp-Tradition quasi hinterrücks zu ermorden. Bedauerlicherweise hatte ich persönlich nie die Gelegenheit, mit einem Interlisp-System zu arbeiten, weshalb ich nicht beurteilen kann, ob an diesen Sorgen etwas dran war bzw. ist oder nicht.


Scheme und Lisp

Ist Lisp eine funktionale Programmiersprache?

(Of course SML does have its weaknesses, but by comparison, a discussion of C++'s strengths and flaws always sounds like an argument about whether one should face north or east when one is sacrificing one's goat to the rain god.) — Thant Tessman

Ja und nein. Wenn jemand Lisp als funktionale Programmiersprache bezeichnet, dann geht es wahrscheinlich um Scheme, in welchem tatsächlich oft das funktionale Paradigma angewandt wird. Common-Lisp-Programmierer schreiben ihren Code viel weniger in funktionalem Stil und bedienen sich bei allen möglichen Paradigmen je nach Situation. Da Common Lisp ein mächtiges eingebautes Objektsystem hat, sind moderne Lispprogramme meist in einer Kombination aus funktionalem, imperativem und objektorientiertem Stil geschrieben.

Der Punkt, den man sich hier merken sollte, ist, daß es nicht viel bedeutet, daß eine Programmiersprache funktional ist. Funktionen als Objekte wie alle anderen zu behandeln, ist eine Sache (und in der Tat unterstützen das alle Dialekte der Lisp-Familie); ob Code in der jeweiligen Sprache aber auch in funktionalem Stil geschrieben wird, hängt mehr von den Menschen ab, von denen die Sprache verwendet wird, als von den rein technischen Eigenschaften der Sprache selbst.

Wie stark unterscheiden sich Scheme und Common Lisp wirklich?

How many Schemers does it take to change a light bulb? Eventually a Common Lisp programmer changes it because the Schemers were all worried about the side-effects! — Sohail Somani, comp.lang.lisp

Lest they think we're mean-spirited about them, I sort of imagine the Schemers probably have the reverse sentiment... How many CL programmers does it take to change a light bulb? None. They don't want to be enlightened. — Kent Pitman, followup to the above

Die Unterschiede zwischen Common Lisp und Scheme sind nicht nur technischer, sondern vor allem sozialer und politischer Natur. Zwei Sprachen, die ähnlich anfangen, aber von ganz unterschiedlichen Gemeinschaften weiterentwickelt werden, driften mit der Zeit immer weiter auseinander.

Wo Lisper Praxistauglichkeit hervorheben, heben Schemer Eleganz und Kompaktheit hervor, und wo Lisper Interaktivität hervorheben, betonen Schemer die klare Semantik von Programmtext. Solche Unterschiede in den Ansichten äußern sich natürlich entsprechend in den verfügbaren Entwicklungsumgebungen, Bibliotheken und Programmiermethodiken der beiden Lager.

Vereinzelt wird sogar die Meinung geäußert, Scheme habe sich so weit vom Geist von Lisp entfernt, daß es nicht mehr als ein Lisp zu bezeichnen sei. (Umgekehrt wurde dasselbe auch schon über Common Lisp gesagt — primär von Interlisp-Anhängern.)

Historische Notiz: Mittlerweile hört man sogar aus der Scheme-Gemeinde selbst Stimmen, die die neueste Inkarnation des Scheme-Standards, R6RS, nicht mehr als ein Lisp anerkennen. Zugleich haben es sich einige Kritiker zur Aufgabe gemacht, einen Alternativstandard namens ERR5RS zu entwerfen, um dieser Entwicklung entgegenzuwirken. Wie die Auswirkungen dieser neuartigen Kontroverse aussehen werden und was sie für die Scheme-Gemeinde bedeuten, ist noch ungewiß.

Wo soll ich mich umsehen, wenn ich mich für Scheme interessiere?

Erst einmal ist es eine gute Idee, sich eine bestimmte Scheme-Implementierung auszusuchen, da es nicht viel Sinn hat, zu versuchen, sich allein an den Standard zu halten, der bekanntlich sehr klein ist und allein wenig Funktionalität bietet. Gute Kandidaten dafür sind DrScheme, Chicken Scheme, MIT-Scheme, Scheme48 und GNU Guile, wobei DrScheme wohl über die anfängerfreundlichste Entwicklungsumgebung verfügt.

Websites von Interesse sind Readscheme.org, schemers.org und das SchemeWiki.

newLISP hört sich auch nach einer Alternative an. Ist es eine?

About the use of language: it is impossible to sharpen a pencil with a blunt axe. It is equally vain to try to do it with ten blunt axes instead. — Edsger Dijkstra, How do we tell truths that might hurt?

Die kurze Antwort: nein.

Der Hauptfehler von newLISP ist sein Name: Er müßte eigentlich oldLISP lauten.

newLISP führt viele Ideen wieder ein, die die Lispcommunity nach langem Ringen mit sich selbst letztlich als fehlgeleitet erkannt und hinter sich gelassen hat. Dazu gehören z.B. die dynamische Variablenbindung als Vorgabe und das Fehlen von Makros zugunsten von Funktionen, die ihre Argumente nicht auswerten (FEXPRs, wie sie im alten MACLISP hießen). Außerdem macht es teilweise Kompromisse von einer Art, die sowohl von Lispern als auch von Schemern höchstens müde belächelt werden (z.B. das Fehlen einer richtigen automatischen Speicherverwaltung mit dem ständigen tiefen Kopieren ganzer Datenstrukturen als scheinbar weniger aufwendige Alternative dazu).

Insgesamt wiederholt newLISP lediglich die Fehler der Vergangenheit und bietet zugleich nichts wesentlich Neues — jedenfalls aus Sicht der heutigen Lispgemeinde.

Sicherlich: Wer weiß — vielleicht sind newLISP und mehr noch Pico Lisp ja auch trübe Fenster in eine andere Welt, in der die vergessenen Traditionen von Sprachen wie Interlisp weiterleben, welche ihre besten Zeiten erst noch vor sich haben.

Doch für hier und jetzt gilt: Wenn man die Lisp-Erfahrung von vor einem Vierteljahrhundert erleben möchte, kann man sich newLISP ansehen. In allen anderen Fällen würde ich persönlich davon eher abraten.

Bemerkung: Das alles soll nicht heißen, daß newLISP nicht seinen Platz in der Werkzeugkiste eines guten Handwerkers hat. Es ist nach Aussagen seines Urhebers auch gar nicht als Konkurrenz zu Common Lisp und Scheme gedacht, sondern rein zum »Scripting«, wofür es sich gut eignen mag oder auch nicht. Alles, was ich sagen möchte, ist, daß man als Lisp-Neuling nicht newLISP lernen sollte, weil es einem einen völlig falschen, nämlich anachronistischen Eindruck vom heutigen Stand von Lisp vermittelt.


Ressourcen für den Einstieg

Nachdem hoffentlich die brennendsten Fragen beantwortet wurden, kommen wir nun zu den Informationsquellen, die man als Einsteiger benötigt.

Die Erstinstallation einer Lispumgebung

Der Lispcompiler und SLIME

Als erstes sollte man sich eine passende Lispumgebung installieren (siehe auch den nächsten Abschnitt über die Wahl eines solchen Systems).

Unter Windows sollte man es für den Anfang mit der Personal Edition von LispWorks und Edi Weitz' STARTER-PACK versuchen. Es konfiguriert ASDF automatisch und installiert einige oft genutzte Bibliotheken gleich mit.

Debian- und Ubuntu-Anwender haben es relativ leicht. Sie können über die normalen Wege einen Lispcompiler installieren, die ihre Distribution ihnen anbietet, z.B. für SBCL:

sudo apt-get install sbcl

ASDF-Systeme können ebenfalls mit apt-get installiert werden, z.B. so:

sudo apt-get install cl-ppcre

Danach kann man das Lisp starten und z.B. folgendes eingeben, um CL-PPCRE zu laden:

(clc:clc-require :cl-ppcre)

Der Debianpaketname muß allerdings nicht unbedingt immer mit dem Systemnamen übereinstimmen. Manchmal ist es nötig, in der CLC:CLC-REQUIRE-Zeile z.B. das »cl-« im Systemnamen wegzulassen.

Welche Bibliotheken mit apt-get installierbar sind, kann man herausfinden, indem man sich der Fähigkeiten von apt-cache bedient (übrigens ganz allgemein ein sehr nützliches Werkzeug):

apt-cache search '^cl-'

Es ist in jedem Falle eine gute Idee, auch SLIME zu installieren:

sudo apt-get install slime

Wenn man das getan hat, kann man SLIME von Emacs aus mit M-x slime [RET] (d.h. Alt-X, gefolgt von »slime« und der Bestätigung durch die Eingabetaste) starten. (Je nach Lispimplementierung funktioniert C-u M-x slime [RET], d.h. Control-U, Alt-X, »slime«, <RETURN>, besser, weil es zusätzlich nach dem Namen des gewünschten Lispcompilers fragt. Hier kann man z.B. »sbcl« eingeben.)

Allen anderen empfehle ich, clbuild zu verwenden, ein Skript, das unter unixoiden Betriebssystemen eine ähnliche Aufgabe erfüllt wie STARTER-PACK unter Windows. Es kann zusätzlich bei Bedarf SBCL herunterladen und installieren. SLIME kann durch Aufruf von clbuild slime gestartet werden. Eine Alternative dazu bietet Lispbox, das auch gleich ein ganzes, vorkonfiguriertes Emacs mitbringt.

ASDF und ASDF-INSTALL

Verwendung

ASDF-INSTALL erlaubt es, Systeme (d.h. Bibliotheken und Anwendungen) automatisch herunterzuladen und mitsamt ihrer Abhängigkeiten zu installieren.

Wenn ASDF-INSTALL installiert, eingerichtet und geladen wurde, kann man z.B. CL-PPCRE installieren, indem man folgendes eingibt:

(asdf-install:install :cl-ppcre)

Um ein bereits installiertes System (hier CL-PPCRE) zu laden:

(asdf:oos 'asdf:load-op :cl-ppcre)

In SBCL (aber nur dort!) darf man sich die letztere umständliche Inkantation zum Laden von ASDF-Systemen sparen und stattdessen REQUIRE verwenden:

(require :cl-ppcre)

Zum Installieren neuer Systeme gibt es jedoch auch in SBCL nur den »normalen« Befehl.

Einrichtung

Das ASDF-INSTALL-Tutorial beschreibt, wie man ASDF und ASDF-INSTALL in anderen Lispumgebungen als SBCL einrichtet.

Wenn man SBCL verwendet, muß man sich keine großen Sorgen machen, da es sowohl ASDF als auch ASDF-INSTALL in der Standardinstallation mitbringt. In diesem Fall genügen jeweils die folgenden Zeilen zum Laden.

Für ASDF:

(require :asdf)

Für ASDF-INSTALL:

(require :asdf-install)

Welche Common-Lisp-Implementierung ist die richtige für mich?

Im Falle von Common Lisp ist die Wahl der Implementierung weit weniger wichtig als für Scheme, da man leicht portablen Code schreiben kann, der auf allen gleichermaßen läuft. Am besten orientiert man sich an der Entwicklungsumgebung und der gewünschten Lizenz.

Es gibt zeitlich unbegrenzte kostenlose Versionen für den unkommerziellen Gebrauch von Allegro CL und LispWorks, welche beide mit hochwertigen Entwicklungsumgebungen und ausführlichster Dokumentation daherkommen und für Programmierer ohne Emacs-Erfahrung (und solche mit negativer Emacs-Erfahrung) geeignet sind.

Windowsanwender sollten es vielleicht zuerst mit LispWorks versuchen und Edi Weitz' STARTER-PACK installieren. Es übernimmt die Konfiguration von ASDF und erlaubt es, eine Menge nützlicher Bibliotheken gleich mitzuinstallieren (siehe den vorangehenden Abschnitt über die Einrichtung einer Lispumgebung).

Wenn man freie Software bevorzugt, bietet sich erst einmal SBCL an, welches auf vielen Plattformen läuft und von der Emacs-gestützten Entwicklungsumgebung SLIME gut unterstützt wird. Sein Hauptmanko ist die noch in den Kinderschuhen steckende Windowsversion, die für viele ein K.O.-Kriterium sein kann. (Aber nicht vergessen: Es ist normalerweise kein Problem, in SBCL zu entwickeln und das fertige Programm später mit einem Windows-freundlicheren Lisp auszuliefern.)

Mac-OS-X-Anwender könnten Clozure CL interessant finden. Im Gegensatz zu SBCL unterstützt es auch unter Mac OS X Multithreading und erlaubt es, native GUI-Anwendungen direkt mit Cocoa zu implementieren. Es erlaubt außerdem den Zugriff auf alle anderen einem Objective-C-Programmierer zur Verfügung stehenden Bibliotheken.

Wer ein freies Lisp für Windows sucht, kann GNU CLISP verwenden. Leider ist die SLIME-Unterstützung für CLISP nicht die allerbeste, und CLISP kompiliert in Bytecode, nicht in Maschinencode. Letzteres ist sicher für die Entwicklung selbst kein großes Problem, Ersteres kann allerdings schon etwas stören.

Welches System auch immer man bevorzugt, vom Autor des online lesbaren Lispbuchs Practical Common Lisp gibt es ein vorgefertigtes, Emacs-basiertes Paket namens Lispbox, das speziell für Anfänger und zur Verwendung zusammen mit dem Buch eingerichtet ist. Als emacslose Alternative bietet sich die ebenfalls als freie Software verfügbare anfängerfreundliche Entwicklungsumgebung ABLE an.

Abraten möchte ich an dieser Stelle übrigens dringend von GNU Common Lisp (GCL), einem Lispcompiler, der auf einer veralteten Version der Sprache Common Lisp basiert. Ihm fehlen wichtige Entwicklungswerkzeuge, und nur wenige neu entwickelte Bibliotheken unterstützen ihn. GCL hat seinen Platz, aber ein Anfänger sollte sich ein anderes Lispsystem aussuchen. Keinesfalls sollte man GNU Common Lisp mit GNU CLISP verwechseln.

Tutorien und Bücher

Die empfehlenswerteste Einführung in Common Lisp, die sowohl kostenlos im Internet lesbar als auch als gedrucktes Buch verfügbar ist, ist Practical Common Lisp von Peter Seibel.

Für ambitionierte Fortgeschrittene eignet sich Paul Grahams ebenfalls kostenlos herunterladbares Buch On Lisp, das dem Leser einige Techniken und Kniffe näherbringt, die in den meisten Einführungen nicht behandelt werden.

Ein weiteres auch online lesbares Buch ist Successul Lisp von David Lamkins. Es ist nicht so neu wie Practical Common Lisp und wird nicht so häufig empfohlen. Obwohl es weniger praxisnah und für programmierunerfahrene Menschen ungeeigneter ist, ist es einen Blick wert, wenn man einen alternativen Blickwinkel sucht.

Nachschlagewerke

Was man unbedingt immer zur Hand haben sollte, ist die CLHS (Common Lisp HyperSpec), die im Grunde eine nicht streng offiziell, aber jedenfalls de facto fast genau dem Text des ANSI-Standards entsprechende Referenzdokumentation der Sprache Common Lisp darstellt. Sie hat einen etwas eigenen Schreibstil, aber es gibt Leute, die ihre Klarheit und Präzision allem anderen gegenüber bevorzugen. Für eine Spezifikation ist sie zweifelsohne ungewöhnlich leserfreundlich.

Normalerweise bieten gute Lisp-Entwicklungsumgebungen immer einen schnellen Zugriff auf die CLHS, indem man den Cursor auf den fraglichen Klassen/Funktions/etc.-Namen bewegt und eine bestimmte Tastenkombination drückt oder einen Menüpunkt anwählt.

Wenn man nicht genau weiß, wonach man eigentlich sucht, kann man das Inhaltsverzeichnis des ANSI and GNU Common Lisp Document aufschlagen und ein bißchen herumsuchen (z.B. mithilfe der Suchfunktion des Webbrowsers). Ebenso wie die CLHS entspricht es vom Inhalt her dem ANSI-Standard, ist aber etwas anders aufbereitet.

Ein weiteres Werk, in das man hin und wieder einen lohnenden Blick werfen kann, ist Guy Steeles Common Lisp the Language (zweite Ausgabe). Zwar beschreibt CLtL2, wie man das Buch kurz bezeichnet, eine nicht exakt dem ANSI-Standard entsprechende Version der Sprache, da es vor der Vollendung des ANSI-Standards geschrieben wurde, aber sichtbare Abweichungen finden sich nur an Stellen, auf die ein Neuling eigentlich nicht so schnell stoßen sollte.

LOOP

Einige Adressen zu LOOP, welches eine eigene in Lisp eingebettete Schleifensprache ist:

Unterstützung durch Gleichgesinnte und Experten

Wer Unterstützung oder Antworten auf eine konkrete Frage sucht, wendet sich am allerbesten an die Newsgroup comp.lang.lisp. Allen Gerüchten zum Trotz sind die Leute dort wirklich sehr nett und helfen gern.

Da es dort leider auch eine nichttriviale Zahl an Trollen (d.h. Ärgermachern) gibt, kann es durchaus passieren, daß einige comp.lang.lisp-Mitglieder etwas defensiv reagieren, wenn Fragen gestellt werden, die folgende Symptome teilweise oder ganz aufweisen:

Keine Sorge, es ist nicht schwierig, diese Art von Nichtfragen zu vermeiden, und Neulinge sind in comp.lang.lisp ansonsten sehr willkommen.

Man sollte freilich außerdem die überall gültige Usenet-Etiquette beachten und möglichst zeigen, inwiefern man sich selbst schon mit der gestellten Frage beschäftigt hat. Jemand, der jede Frage in einer Newsgroup stellt, ohne vorher wenigstens in Google nach einer Antwort gesucht zu haben, stößt im Usenet grundsätzlich auf wenig Gegenliebe.

Eine Bemerkung am Rande: comp.lang.lisp ist wahrscheinlich der einzige Ort im ganzen Internet, an dem man Mitglieder einer Technologiegemeinde jeglicher Generation treffen kann. Sowohl junge Lisper als auch erfahrene Veteranen, die in Lispmaschinenfirmen gearbeitet haben oder gar am Design einflußreicher Lisp-Dialekte beteiligt waren und alle möglichen Dialekte in der Geschichte von Lisp als Anwender oder Implementor kennengelernt haben, frequentieren comp.lang.lisp. Historische Fragen werden dort dementsprechend genauso gern diskutiert wie technische, und da die lange Tradition von Lisp für den geneigten Leser viele wissenswerte Details bereithält, ist die Newsgroup für Neulinge und Erfahrene gleichermaßen ein guter Ort, um Zeit totzuschlagen und das tatsächliche Schreiben von Code möglichst lange hinauszuzögern. Das darf man sich aber ruhig auch hin und wieder gönnen, denn: Was zählt, ist der Spaß.

Bibliotheken für GUIs, Sockets et cetera

Um Bibliotheken und anderen Code zu finden, sucht man am besten im CLiki und im Common-Lisp-Verzeichnis. Letzteres enthält auch Links zu verschiedenen Arten von Dokumenten.

Meine persönlichen, konkreten Empfehlungen für Bibliotheken sind:

Wenn man sein Lisp, wie oben vorgeschlagen, mit clbuild oder STARTER-PACK eingerichtet hat, kann man einige dieser Bibliotheken jeweils damit installieren.


Klassische Stolpersteine

Ich verstehe das Paketsystem nicht ganz...

If C++ has taught me one thing, it's this: Just because the system is consistent doesn't mean it's not the work of Satan. — Thant Tessman

Das Paketsystem ist sicher einer der größten Stolpersteine für einen Neuling, der bereits andere Programmiersprachen beherrscht, da es den Intuitionen eines Nichtlispers in mancherlei Hinsicht widerspricht und der Verwirrnis gleich mehrere Gelegenheiten auf einmal bietet, zuzuschlagen.

Zuerst einmal ist es das das Wichtigste, sich klarzumachen, was das Paketsystem eigentlich ist. Man muß zwischen zwei voneinander getrennten Dingen unterscheiden, die man leicht verwechseln kann:

  1. Ein System ist eine Sammlung von Code (also z.B. eine Menge von Quelldateien oder Objektcode), die geladen werden kann, um bestimmte Funktionen, Klassen, Methoden und anderes zur Verfügung zu stellen. Ein System ist also das, was man in anderen Sprachen als Bibliothek oder Modul bezeichnet.
  2. Ein Paket ist ein Namensraum für Funktions-, Klassen- und Variablennamen.

Es ist sehr wichtig, diese beiden Dinge gedanklich nicht miteinander zu vermischen, weil sie a priori gar nichts miteinander zu tun haben. Insbesondere kann man Pakete nicht »laden«, doch ein System kann, während es geladen wird, ein oder mehrere Pakete erzeugen (muß aber nicht).

In Ordnung. Wie funktioniert also das Paketsystem?

Der zentrale Begriff im Paketsystem ist das eines Symbols. Ein Symbol ist ein Name für eine Funktion, eine Klasse oder eine Variable. Beispielsweise gibt es das Symbol LIST im Paket COMMON-LISP, welches der Name für eine bestimmte standardisierte Funktion ist. Zugleich bezeichnet es aber auch eine standardisierte Klasse. Nur LIST zu schreiben, ist allerdings ohne Kontext mehrdeutig, weil es mehrere Symbole mit dem Namen LIST geben kann, und zwar in unterschiedlichen Paketen. Die vollständige, eindeutige Bezeichnung für das Symbol LIST im Paket COMMON-LISP ist COMMON-LISP::LIST oder alternativ CL::LIST. Der Teil vor den Doppelpunkten wird Paketpräfix genannt.

Wenn man ein Paket erzeugt, kann man Symbole aus anderen Paketen importieren (mithilfe von :IMPORT oder :USE, siehe die Beschreibung zu defpackage). Dadurch werden sie im neuen Paket verfügbar, und man muß keinen Paketpräfix angeben, wenn man sich von dem neuen Paket aus auf sie bezieht. Es ist aber wichtig, sich klarzumachen, daß man beim Import von CL::LIST nicht etwa nur die Funktion importiert, die mit CL::LIST bezeichnet wird, sondern das Symbol selbst. Wenn das neue Paket also MEIN-PAKET heißt, so bezeichnen fortan MEIN-PAKET::LIST und CL::LIST dasselbe Symbol. Insbesondere darf man dann nicht selbst auch eine Funktion namens MEIN-PAKET::LIST definieren, weil man sonst die Funktion CL::LIST umdefiniert!

Merke: Das Paketsystem arbeitet mit Symbolen und nur mit Symbolen. Was die Symbole bezeichnen, oder ob sie überhaupt etwas bezeichnen, ist dem Paketsystem egal.

Oft kommt es ganz unerwartet zu Konflikten beim nachträglichen Importieren von Symbolen oder ganzen Paketen mit use-package (oder beim Neuladen einer veränderten defpackage-Form). Um diese Konflikte zu verstehen, muß man wissen, wie das Paketsystem mit dem Leser (also dem Teil von Lisp, der den Code einliest und daraus Datenstrukturen macht, die der Compiler verstehen kann) interagiert. Eine unbedingt lesenswerte Erklärung dessen, was da passiert, die der Autor aus eigener Erfahrung empfehlen kann, ist The Complete Idiot's Guide to Common Lisp Packages von Ron Garret. Da es für die Frustfreiheit von Lispprogrammierung sehr wichtig ist, das Paketsystem verstanden zu haben, sollte man sich die Zeit nehmen, den genannten Aufsatz aufmerksam zu lesen und mit den gebrachten Beispielen selbst ein bißchen herumzuspielen.

Und wie war das mit diesen »Systemen«? Bibliotheken, sagtest Du?

Wie Systeme funktionieren, also Sammlungen von Code, die andere Sprachen Module oder Bibliotheken nennen würden, ist nicht im ANSI-CL-Standard festgelegt (obschon es dazu einen Standardisierungsvorschlag gab). Heutzutage gibt es aber glücklicherweise einen de-facto-Standard, an den sich alle Neuveröffentlichungen halten: Another System Definition Facility (ASDF).

Wie man ASDF einrichtet und verwendet, wird im Abschnitt über die Erstinstallation eines Lispsystems beschrieben.

»System« ist ein recht allgemeiner Begriff. Wenn man selbst Code schreibt, dann wird man dafür auch ein System mit ASDF definieren wollen, das die zugehörigen Dateien und Abhängigkeiten gegenüber anderen Systemen beschreibt, die von ASDF geladen werden sollen. Man macht das üblicherweise nicht nur für Bibliotheken, sondern auch für Anwendungen.


Das Fremde kennenlernen

Warum die klammerlastige Syntax?

Lisp has all the visual appeal of oatmeal with fingernail clippings mixed in. — Larry Wall

Es gab schon mehr Vorschläge einer konventionelleren Syntax für Lisp, als man zählen kann, und doch hat sich keine davon durchgesetzt. Auch für McCarthy, der Lisp in den 50er Jahren erfunden hatte, kam das als Überraschung, da er selbst noch an einer alternativen Syntax arbeitete.

Der Grund dafür ist die ungeheure Macht, die eine so einfache Syntax — oder eigentlich: das Fehlen einer Syntax — mit sich bringt. Man kann kaum überbetonen, wie sehr die Gleichbehandlung bzw. Vereinheitlichung von Code und Daten von der auf den ersten Blick eigenartigen Syntax abhängt und welche Rolle ebendiese Vereinheitlichung für das Programmieren in Lisp spielt.

Ein weiterer, seltener genannter Vorteil der Syntax von Lisp ist die Möglichkeit, einen Editor zu verwenden, der auf der Ebene von Ausdrücken arbeitet statt auf der Ebene von Zeichen. In der Vergangenheit gab es sogar Struktureditoren für Lispcode, auf die einige Interlisp-Fans nach wie vor schwören. Doch auch weniger radikale Ansätze wie der Paredit-Modus für Emacs machen die Navigation in und Arbeit mit S-Ausdrücken, wie sie auch genannt werden, für den Geübten angenehmer als jeder noch so komfortable Editor für eine andere Sprache.

Mit diesem Punkt hängt auch zusammen, daß viele Lisper darauf hinweisen, daß für sie die Klammern quasi verschwinden, wenn sie den Code ansehen. Die Klammern sind ja lediglich eine visuelle Stütze beim Betrachten von Code, während die Struktur in aller Regel durch die Formatierung offenbart wird. Daher ist es auch wichtig, Lispcode entweder immer richtig einzurücken oder den Editor das Einrücken übernehmen zu lassen. (Emacs ist in der Hinsicht kaum zu übertreffen.)

Neulinge stellen die Frage nach der Syntax in der Regel aus einem von zwei Gründen.

Der erste Grund ist, daß sie sich von anderen Sprachen her so daran gewöhnt haben, daß nur komplexe Ausdrücke geklammert werden, daß sie mit Klammern ganz automatisch und unbewußt unverständlichen Code assoziieren. In der Tat sind viele Klammern in anderen Sprachen ein Zeichen dafür, daß der Code schlecht durchdacht ist und wahrscheinlich vereinfacht werden kann. Dies ist aber fast immer ein Symptom von komplizierten Operatorvorrangsregeln, welche es in Lisp naturgemäß nicht gibt.

Der zweite Grund ist, daß Neulinge oft Bedenken darüber haben, ob das Fehlen von visuellen Hinweisen in ihrem Quelltext, ja die einheitliche Notation schlechthin wohl zur Folge haben könnte, daß verschiedene Typen von Ausdrücken schwieriger zu unterscheiden sind als in anderen Sprachen. Tatsächlich spielt das Unterscheiden von Ausdruckstypen in Lisp gar keine große Rolle, auch wenn es anfangs seltsam erscheinen mag, daß eine »if«-Abfrage dieselbe Syntax hat wie ein Funktionsaufruf. Dennoch heben viele Texteditoren auch in Lispcode Sonderausdrücke farblich oder durch Fettdruck hervor, wodurch auch diese Bedenken sich mit ein bißchen Übung in Luft auflösen.

Ist die große Freiheit in Lisp nicht schädlich für Teams?

Just because we Lisp programmers are better than everyone else is no excuse for us to be arrogant. — Ron Garret

Die Tatsache, daß Programmierer ihre eigenen Sonderausdrücke definieren können, schreckt manch einen ab, der es von Sprachen wie Java her kennt, daß man Programmierer durch den Compiler dazu zwingen möchte, nicht unlesbaren Spaghetticode aus lauter selbstdefinierten Konstrukten zu schreiben. In der Praxis erweisen sich diese Befürchtungen aber zumeist als unbegründet, weil der Programmierer mit der neu gewonnenen Freiheit in der Lage ist, seine Gedanken so aufzuschreiben, daß Wiederholungen vermieden werden und der Code beträchtlich an Übersichtlichkeit gewinnt.

Und seien wir mal ehrlich — schlechte, undisziplinierte Programmierer, die sich nicht an Codingrichtlinien halten, sind für jedes Team eine Katastrophe, egal, welche Sprache verwendet wird. Diesbezügliche Horrorgeschichten sind gerade in der industriellen Java-Welt keine Seltenheit. Es gibt also keinen Grund, anzunehmen, daß sich das Problem durch virtuelle Zwangsjacken mildern läßt.

In jüngerer Zeit zeigt der Erfolg von Sprachen wie Ruby und Python, die aufgrund ihrer Inspiration durch Lisp ebenfalls scheinbar unkontrollierbare Macht mit strukturierter Einfachheit verbinden, daß die praktischen Probleme praktizierter Freiheit lange nicht so groß sind, wie man erwarten könnte.

Zuguterletzt: Sollte es doch einmal nötig sein, so kann man für Lisp aufgrund des Fehlens von Syntax vergleichsweise leicht Codeprüfungswerkzeuge schreiben, die das Einhalten von Codingstandards kontrollieren. Lisp macht einem das einfach. Kann man dasselbe über Java sagen?

Warum die anachronistischen Namen?

I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. — Alan Kay

Manchmal erscheinen einem die unüblichen (lambda list) und teilweise anachronistischen (CAR und CDR) bis hin zu regelrecht gruseligen (UNWIND-PROTECT) Bezeichnungen, die manche Konzepte und Operatoren in Common Lisp haben, als unnötige Hürde. Diese Bedenken sind durchaus berechtigt, und der Wert eines obskuren Namens wie TERPRI ist durchaus anzweifelbar. Zugleich kann man aber insofern beruhigt sein, daß niemand dazu gezwungen ist, jemals CAR und CDR in seinen Programmen zu verwenden, da es auch die Synonyme FIRST und REST gibt — und selbst gäbe es sie nicht, wäre es ein Leichtes, sie sich selbst zu definieren (dies gilt auch für Sonderausdrücke!).

Die Gründe für diese scheinbaren Anachronismen liegen einerseits in der Kompatibilität mit altem Code, andererseits aber auch im traditionsbewußten Denken der Lispgemeinde. Wer sich einmal an den teilweise recht schön klingenden, vor allem aber technisch präzisen Jargon gewöhnt hat, möchte ihn nur noch ungern aufgeben.

Was man indes nie vergessen darf, ist, daß jede Disziplin ihren eigenen Jargon entwickelt, der zugleich einen kulturellen und politischen (nämlich das Sich-Hineindenken in eine bestimmte Kultur bzw. ein bestimmtes Denkmuster) wie einen technischen Wert (nämlich Präzision) hat. Keinen dieser beiden Werte sollte man mißachten, wenn man scheinbar übermäßige Nutzung von Fachausdrücken durch Experten eines Gebiets untereinander beanstandet.

Wie in vielen Aspekten heißt es also auch hier: Augen zu und durch! Wenn man sich auf die fremde Kultur einläßt statt sich vor ihr zu verschließen, wird alles mit einem Mal halb so wild.

Wie erstelle ich ein ausführbares Programm (z.B. eine .exe-Datei)?

Some people, when confronted with a [programming] problem, think, “I know, I'll use regular expressions.” Now they have two problems. — Jamie Zawinski

Die einzig hilfreiche, wenngleich wahrscheinlich unbefriedigende Antwort für einen Neuling ist hier der Ratschlag, erst einmal das betreffende Programm zu schreiben und erst anschließend darüber nachzudenken, wie man es ausliefert. Ich möchte nicht ins Detail gehen, weil sich die Prozedur von Compiler zu Compiler unterscheidet, und es wirklich viel, viel einfacher ist, sie zu verstehen, wenn man in Lisp bereits programmieren kann.

Der entscheidende Punkt, der zu diesem Zeitpunkt als Information reichen sollte, ist, daß es geht, und zwar sowohl unter Windows als auch unter anderen Betriebssystemen, aber daß es nicht annähernd so funktioniert, wie man als Neuling erwartet, und es besser ist, sich die konkreten Vorgänge für später aufzuheben.

Wird Lisp irgendwo praktisch angewandt?

I suppose I should learn Lisp, but it seems so foreign. — Paul Graham, November 1983

Franz, Inc. hat auf ihrer Firmenwebsite eine ziemlich lange Liste von Erfolgsgeschichten über den Einsatz von Lisp in der Industrie. Viele der Mitglieder von comp.lang.lisp verwenden Lisp auch im Beruf oder haben gar eine eigene Firma darüber aufgebaut. Paul Graham ist ein bekannter Unternehmer, der mit Lisp eine Menge Geld verdient hat.

Common Lisp ist ein Standard, der von Firmen vorangetrieben wurde. Der Schwerpunkt des Standards lag von Anfang an auf praktischer Anwendung im kommerziellen Bereich. Entsprechend gut unterstützt er diesen Einsatzzweck.


Häufige Mißverständnisse

Ist »CLISP« eine Abkürzung für Common Lisp?

There's an aura of unholy black magic about CLISP. It works, but I have no idea how it does it. I suspect there's a goat involved somewhere. — Johann Hibschman, comp.lang.scheme

Nein. CLISP steht für GNU CLISP, einen ganz bestimmten Lispcompiler — und im übrigen keinen besonders charakteristischen. Er ist zum Beispiel der einzige heute relevante Lispcompiler, der keinen Maschinencode erzeugt, sondern Bytecode.

Ist Lisp langsam?

A Lisp programmer knows the value of everything, but the cost of nothing. — Alan Perlis, Epigrams on Programming

Früher waren fast alle Lispimplementierungen Interpreter, doch diese Zeiten sind längst vorbei. Lisp ist heute eine der wenigen Sprachen, die sowohl interaktiv sind als auch zu schnellem Maschinencode kompiliert werden.

Manche Implementierungen verwenden Interpreter und Compiler zugleich (z.B. CMUCL), andere haben nur noch einen Compiler (z.B. SBCL). Die einzige heutzutage relevante Implementierung, die keinen Maschinencodecompiler enthält, ist GNU CLISP. Sie kompiliert stattdessen in portablen Bytecode (ähnlich Java).


Matthias Benkard, 05. April 2008, 22:08 CEST

http://matthias.benkard.de/