Was Lisp anders machte
Dezember 2001 (Überarbeitet Mai 2002)
(Dieser Artikel entstand als Antwort auf einige Fragen auf der LL1 Mailingliste. Er ist nun in Revenge of the Nerds enthalten.)
Als McCarthy Lisp in den späten 1950er Jahren entwarf, war es eine radikale Abkehr von bestehenden Sprachen, von denen Fortran die wichtigste war.
Lisp verkörperte neun neue Ideen:
1. Konditionale Ausdrücke. Ein Konditional ist ein Wenn-Dann-Sonst-Konstrukt. Wir nehmen das heute als selbstverständlich hin. Sie wurden von McCarthy im Zuge der Entwicklung von Lisp erfunden. (Fortran hatte zu dieser Zeit nur ein bedingtes goto, das eng an der Branch-Instruktion der zugrundeliegenden Hardware orientiert war.) McCarthy, der im Algol-Komitee war, brachte Konditionale in Algol ein, von wo aus sie sich auf die meisten anderen Sprachen verbreiteten.
2. Ein Funktionstyp. In Lisp sind Funktionen Objekte erster Klasse – sie sind ein Datentyp wie Ganzzahlen, Zeichenketten usw., haben eine literale Darstellung, können in Variablen gespeichert, als Argumente übergeben und so weiter werden.
3. Rekursion. Rekursion existierte natürlich schon vor Lisp als mathematisches Konzept, aber Lisp war die erste Programmiersprache, die sie unterstützte. (Sie ist wohl implizit darin enthalten, Funktionen zu Objekten erster Klasse zu machen.)
4. Ein neues Variablenkonzept. In Lisp sind alle Variablen effektiv Zeiger. Werte sind das, was Typen hat, nicht Variablen, und das Zuweisen oder Binden von Variablen bedeutet das Kopieren von Zeigern, nicht von dem, worauf sie zeigen.
5. Garbage-Collection.
6. Programme, die aus Ausdrücken bestehen. Lisp-Programme sind Bäume von Ausdrücken, von denen jeder einen Wert zurückgibt. (In einigen Lisps können Ausdrücke mehrere Werte zurückgeben.) Dies steht im Gegensatz zu Fortran und den meisten nachfolgenden Sprachen, die zwischen Ausdrücken und Anweisungen unterscheiden.
Es war natürlich, diese Unterscheidung in Fortran zu haben, da (nicht überraschend in einer Sprache, deren Eingabeformat Lochkarten waren) die Sprache zeilenorientiert war. Man konnte keine Anweisungen verschachteln. Und so, während man Ausdrücke für die Mathematik benötigte, damit sie funktioniert, gab es keinen Sinn, irgendetwas anderes einen Wert zurückgeben zu lassen, da niemand darauf wartete.
Diese Einschränkung verschwand mit dem Aufkommen blockstrukturierter Sprachen, aber da war es zu spät. Die Unterscheidung zwischen Ausdrücken und Anweisungen war etabliert. Sie verbreitete sich von Fortran auf Algol und von dort auf beide ihre Nachfolger.
Wenn eine Sprache vollständig aus Ausdrücken besteht, kann man Ausdrücke beliebig zusammensetzen. Man kann entweder (in Arc Syntax) sagen:
(if foo (= x 1) (= x 2))
oder
(= x (if foo 1 2))
7. Ein Symboltyp. Symbole unterscheiden sich von Zeichenketten dadurch, dass man die Gleichheit durch den Vergleich eines Zeigers testen kann.
8. Eine Notation für Code unter Verwendung von Symbolbäumen.
9. Die gesamte Sprache ist immer verfügbar. Es gibt keine wirkliche Unterscheidung zwischen Lesezeit, Kompilierzeit und Laufzeit. Man kann Code während des Lesens kompilieren oder ausführen, Code während des Kompilierens lesen oder ausführen und Code zur Laufzeit lesen oder kompilieren.
Das Ausführen von Code zur Lesezeit ermöglicht es Benutzern, die Syntax von Lisp neu zu programmieren; das Ausführen von Code zur Kompilierzeit ist die Grundlage für Makros; das Kompilieren zur Laufzeit ist die Grundlage für die Verwendung von Lisp als Erweiterungssprache in Programmen wie Emacs; und das Lesen zur Laufzeit ermöglicht es Programmen, mit S-Ausdrücken zu kommunizieren, eine Idee, die kürzlich als XML neu erfunden wurde.
Als Lisp erfunden wurde, waren all diese Ideen weit von der üblichen Programmierpraxis entfernt, die weitgehend von der verfügbaren Hardware in den späten 1950er Jahren bestimmt wurde.
Im Laufe der Zeit hat sich die Standardsprache, verkörpert in einer Abfolge beliebter Sprachen, allmählich in Richtung Lisp entwickelt. 1-5 sind heute weit verbreitet. 6 beginnt, im Mainstream Fuß zu fassen. Python hat eine Form von 7, obwohl es dafür keine Syntax zu geben scheint. 8, was (zusammen mit 9) Lisp-Makros ermöglicht, ist bisher Lisp vorbehalten, vielleicht weil (a) es diese Klammern oder etwas ähnlich Schlimmes erfordert und (b) wenn man diesen letzten Leistungsschub hinzufügt, kann man nicht mehr behaupten, eine neue Sprache erfunden zu haben, sondern nur ein neues Dialekt von Lisp entworfen zu haben ;-)
Obwohl für heutige Programmierer nützlich, ist es seltsam, Lisp im Hinblick auf seine Abweichung von den zufälligen Notlösungen, die andere Sprachen übernommen haben, zu beschreiben. So hat McCarthy es wahrscheinlich nicht betrachtet. Lisp wurde nicht entwickelt, um die Fehler in Fortran zu beheben; es entstand eher als Nebenprodukt des Versuchs, Berechnungen zu axiomatiseren.