Co czyniło Lispa innym

Grudzień 2001 (zmieniono maj 2002)

(Ten artykuł powstał w odpowiedzi na pytania na liście mailingowej LL1. Został włączony do Revenge of the Nerds.)

Gdy McCarthy projektował Lispa pod koniec lat 50., był on radykalnym odejściem od istniejących języków, z których najważniejszy był Fortran.

Lisp zawierał dziewięć nowych pomysłów:


1. Instrukcje warunkowe. Instrukcja warunkowa to konstrukcja typu jeśli-wtedy-inaczej. Dziś traktujemy je jako coś oczywistego. Zostały one wynalezione przez McCarthy'ego w trakcie opracowywania Lispa. (Fortran w tamtym czasie miał tylko warunkowy goto, ściśle oparty na instrukcji skoku w bazowym sprzęcie.) McCarthy, który był członkiem komitetu Algola, wprowadził instrukcje warunkowe do Algola, skąd rozprzestrzeniły się na większość innych języków.

2. Typ funkcyjny. W Lispie funkcje są obiektami pierwszej kategorii – są typem danych podobnym do liczb całkowitych, ciągów znaków itp., mają reprezentację literałową, mogą być przechowywane w zmiennych, przekazywane jako argumenty i tak dalej.

3. Rekurencja. Rekurencja istniała jako koncepcja matematyczna przed Lispem, oczywiście, ale Lisp był pierwszym językiem programowania, który ją wspierał. (Można argumentować, że jest ona domyślnie zawarta w uczynieniu funkcji obiektami pierwszej kategorii.)

4. Nowa koncepcja zmiennych. W Lispie wszystkie zmienne są efektywnie wskaźnikami. To wartości mają typy, a nie zmienne, a przypisywanie lub wiązanie zmiennych oznacza kopiowanie wskaźników, a nie tego, na co wskazują.

5. Zbieranie śmieci (Garbage-collection).

6. Programy złożone z wyrażeń. Programy Lispa to drzewa wyrażeń, z których każde zwraca wartość. (W niektórych Lispach wyrażenia mogą zwracać wiele wartości.) Jest to sprzeczne z Fortranem i większością późniejszych języków, które rozróżniają wyrażenia i instrukcje.

Posiadanie tego rozróżnienia w Fortranie było naturalne, ponieważ (co nie dziwi w języku, którego format wejściowy opierał się na kartach perforowanych) język był zorientowany na linie. Nie można było zagnieżdżać instrukcji. A zatem, chociaż potrzebne były wyrażenia do działania matematyki, nie było sensu, aby cokolwiek innego zwracało wartość, ponieważ nie było niczego, co by na nią czekało.

To ograniczenie zniknęło wraz z pojawieniem się języków strukturalnych blokowo, ale wtedy było już za późno. Rozróżnienie między wyrażeniami a instrukcjami zostało utrwalone. Rozprzestrzeniło się z Fortranu do Algola, a stamtąd do obu ich potomków.

Gdy język składa się w całości z wyrażeń, można je komponować w dowolny sposób. Można powiedzieć (używając składni Arc):

(if foo (= x 1) (= x 2))

lub

(= x (if foo 1 2))

7. Typ symboliczny. Symbole różnią się od ciągów znaków tym, że można testować równość poprzez porównanie wskaźników.

8. Notacja dla kodu używająca drzew symboli.

9. Cały język zawsze dostępny. Nie ma prawdziwego rozróżnienia między czasem odczytu, czasem kompilacji i czasem wykonania. Można kompilować lub uruchamiać kod podczas odczytu, odczytywać lub uruchamiać kod podczas kompilacji, a także odczytywać lub kompilować kod w czasie wykonania.

Uruchamianie kodu w czasie odczytu pozwala użytkownikom przeprogramować składnię Lispa; uruchamianie kodu w czasie kompilacji jest podstawą makr; kompilacja w czasie wykonania jest podstawą wykorzystania Lispa jako języka rozszerzeń w programach takich jak Emacs; a odczyt w czasie wykonania umożliwia programom komunikację za pomocą s-wyrażeń, pomysłu niedawno wynalezionego na nowo jako XML.


Kiedy Lisp został po raz pierwszy wynaleziony, wszystkie te pomysły były bardzo odległe od zwykłej praktyki programistycznej, która była w dużej mierze dyktowana przez dostępny sprzęt pod koniec lat 50.

Z czasem domyślny język, ucieleśniony w sukcesji popularnych języków, stopniowo ewoluował w kierunku Lispa. Punkty 1-5 są teraz powszechne. Punkt 6 zaczyna pojawiać się w głównym nurcie. Python ma formę punktu 7, chociaż wydaje się, że nie ma dla niego żadnej składni. Punkt 8, który (wraz z punktem 9) jest tym, co umożliwia działanie makr Lispa, jest do tej pory nadal unikalny dla Lispa, być może dlatego, że (a) wymaga tych nawiasów, lub czegoś równie złego, i (b) jeśli dodasz ten ostatni przyrost mocy, nie możesz już twierdzić, że wynalazłeś nowy język, a jedynie zaprojektowałeś nowy dialekt Lispa ;-)

Chociaż jest to przydatne dla współczesnych programistów, dziwne jest opisywanie Lispa w kategoriach jego odmienności od przypadkowych rozwiązań, które przyjęły inne języki. Prawdopodobnie nie tak myślał o tym McCarthy. Lisp nie został zaprojektowany do naprawiania błędów w Fortranie; powstał raczej jako produkt uboczny próby aksjomatyzacji obliczeń.