Common Lisp (często skracane do CL) – dialekt języka programowania Lisp, stworzony przez ANSI (X3.226-1994) jako specyfikacja, nie implementacja. Istnieje kilka implementacji, zarówno zamkniętych, jak i dostępnych jako FOSS. Common Lisp jest wieloparadygmatowym językiem programowania ogólnego przeznaczenia, skupiającym się na programowaniu funkcyjnym, pozwalając jednak stosować obiektowość, co daje programiście dużą swobodę.

Składnia Common Lisp jest dialektem Lispu. Używa S-wyrażeń do opisywania kodu i struktur danych. Funkcje i wywołania makr zapisuje się w postaci list z nazwą funkcji (lub makra) jako pierwszym elementem.

(+ 2 2) ; sumuje 2 i 2, zwraca 4

(setf p 3.1415) ; ustawia wartość zmiennej p na 3.1415, zwraca 3.1415

#+(or) 37 42 ; komentowanie wyrażeń; wynik to 42

; tworzy funkcję podnoszącą liczbę do kwadratu (defun kwadrat (x) (* x x))

; wywołuje powyższą funkcję (kwadrat 3) ; zwraca 9 ; specjalna forma let tworzy nowe zmienne lokalne, których zasięg ogranicza się do końca bloku. (let ((a 6) (b 4)) (+ a b)) ; zwraca 10

Typy danych Typy skalarne Do typów liczbowych należą liczby całkowite, zmiennoprzecinkowe i zespolone oraz ułamki zwykłe. Common Lisp korzysta z dużych liczb do reprezentowania wartości liczbowych o dowolnym rozmiarze i precyzji. Typ ułamkowy nie jest dostępny w większości innych języków. Przy zwracaniu liczby CL automatycznie nadaje jej właściwy typ.

Znaki w CL nie muszą się koniecznie zawierać w standardzie ASCII. Wynika to z faktu, że Lisp został stworzony przed standardem. Niektóre implementacjeImplementacje obsługujące Unikod: CLISP , SBCL , Allegro CL , OpenMCL i możliwie inne. dopuszczają znaki ze standardu Unicode.

Typ symboliczny jest wspólny dla Lispów, ale raczej nieznany poza nimi. Symbol to unikatowy, nazwany obiekt z kilkoma slotami na dane. Symbole w Lispie są często używane podobnie do nazw w innych językach (przechowywanie wartości zmiennej, nazywanie funkcji), jednakże istnieje wiele innych sposobów użycia. W CL w zależności od miejsca użycia symbol zwraca powiązaną z nim funkcję lub wartość. Część symboli przy użyciu zwraca same siebie, na przykład T, NIL czy wszystkie symbole z pakietu KEYWORDS.

Wartości logiczne w Common Lispie reprezentowane są przez symbole T oraz NIL, każda wartość, która nie jest NIL, jest automatycznie uznawana za prawdę.

Struktury danych Do typów sekwencyjnych w CL zaliczają się listy i wektory. Wiele funkcji może pracować na różnych typach sekwencyjnych, na przykład map.

Listy w CL są tworzone za pomocą funkcji cons, czasami skracane do tej nazwy są także komórki cons (, czasami też używa się nazwy para, ang. ). Cons jest strukturą danych z dwoma slotami zwanymi car i cdr. Lista to łańcuch połączonych komórek cons. W liście slot car komórki cons zawiera element listy (na przykład liczbę, znak czy inną listę), a slot cdr wskazuje na następny element listy, lub na symbol NIL, gdy dany dana komórka jest ostatnia w liście. Listy mogą być wykorzystywane do implementacji drzew, grafów i innych struktur danych.

Common Lisp obsługuje wielowymiarowe tablice (array) i potrafi w razie potrzeby dynamicznie zmieniać ich rozmiar. Wielowymiarowe tablice mogą być używane na przykład do implementacji macierzy. Wektor (vector) to jednowymiarowa tablica. W tablicy można przechowywać wszystkie typy danych (nawet różne typy w jednej tablicy) lub można mieć tablice specjalizowane do przechowywania określonego typu, na przykład wektor liczb zmiennoprzecinkowych. W standardzie określone są dwie wyspecjalizowane tablice – string to wektor znaków, a wektor bitowy (bit-vector) to wektor zawierający bity.

Hashe przechowują zależności między kluczami a wartościami tak, by wydajne było pobranie wartości na podstawie klucza.

Przestrzenie nazw (package, znane w niektórych językach jako namespace) są realizowane jedynie dla symboli; parsing niekwalifikowanych ciągów znaków na symbole w przestrzeni nazw odbywa się zwykle podczas kompilacji, ale jeszcze przed semantyczną analizą grafu wyrażeń symbolicznych. Przestrzeń nazw może eksportować symbole, oznaczając je jako zewnętrzny interfejs, jednak ścisła enkapsulacja nie jest w żaden sposób zapewniona. Zawsze możliwe jest odwoływanie się do prywatnych symboli, jak i redefinicja cudzych klas, zmiennych czy we wzorcowych implementacjach samego kompilatora. Jest to filozofia tworzenia oprogramowania wewnątrz maszyny wirtualnej obecna również w Smalltalku, wedle której używane biblioteki, aplikacja, kompilator tworzą organiczną całość.

Struktury, podobnie do struktur C i pascalowych rekordów, reprezentują złożone struktury danych z dowolną liczbą i typem pól (zwanych slotami). Struktury różnią się od zwykłych obiektów z metaklasą standard-class złożonością obliczeniową dostępu do slotu. Przy optymalnej implementacji pobranie wartości zapisanej w strukturze będzie zawsze (1), niezależnie od ilości slotów, jednak za cenę niemożności redefinicji struktur, tudzież konieczności ponownej kompilacji kodu po redefinicji struktury.

CLOS Klasy w Common Lisp Object System posiadają możliwość modyfikacji zachowania obiektów poprzez metaklasy i użycie „protokołu metaobiektowego”, pozwalając na zachowania nie planowane przez twórców, jak np. obiekty automatycznie dodające i uzupełniające się w bazie danych SQLcl-perec czy korzystające z modelu propagacji więzówCells computed-class / przypominającego w działaniu arkusz kalkulacyjny.

Protokół metaobiektowy (MOP) nie został zawarty w standardzie ANSI, jednak książkaThe Art of the Metaobject Protocol, Gregor Kiczales (AMOP) opisująca jego działanie i przykładową implementację stała się de-facto standardem wśród implementacji Lispu.

W przeciwieństwie do języków takich jak Smalltalk czy C++, metody nie należą do przestrzeni nazw klas; należą one do generic function znajdującej się w przestrzeni nazw pakietu (lub modułu, namespace). Możliwa jest specjalizacja typu nie tylko pierwszego argumentu (self, this) ale każdego z wymaganych argumentów. Określanie pasującej metody jest względnie wydajne dzięki sprytnej memoizacji opisanej w AMOP.

Funkcje W CL funkcje są traktowane jako typ danych. Umożliwia to na przykład pisanie funkcji, które pobierają inne funkcje jako argument albo zwracających inne funkcje. Standardowa biblioteka CL szeroko wykorzystuje tę możliwość, na przykład funkcja sort pobiera funkcję porównującą jako argument. Pozwala to na sortowanie nie tylko każdego typu danych, ale także umożliwia sortowanie złożonych struktur danych zależnie od klucza.

(sort (list 5 2 6 3 1 4) #'>) ; Sortuje listę używając > jako operatora porównującego. ; Zwraca (6 5 4 3 2 1).

(sort (list '(9 a) '(3 b) '(4 c)) (lambda (x y) (< (car x) (car y)))) ; Sortuje listę zależnie od pierwszego elementu podlisty. ; Zwraca ((3 b) (4 c) (9 a)). ; Lub: (sort (copy-seq '((9 a) (3 b) (4 c))) #'< :key #'car) ; korzystając z ''keyword parameters'. Model wartościowania dla funkcji jest bardzo prosty. Gdy parser napotyka na formę (F A1 A2 A3 ...), zakłada, że symbol F jest albo: Specjalnym operatorem (z ustalonej listy) Zdefiniowanym wcześniej makrem Nazwą funkcji

Jeżeli F jest nazwą funkcji, argumenty A1, A2, A3 itd. są wartościowane w porządku od lewej do prawej oraz, o ile funkcja istnieje, jest wywoływana z wartościami zwróconymi przez argumenty. Jeśli zaś F desygnuje makro, zostanie ono rozszerzone rekursywnie, aż zostaną jedynie specjalne formy i wywołania funkcji.

Definiowanie funkcji Makro defun służy do definiowania funkcji. Definicja zawiera nazwę funkcji, nazwy jej argumentów oraz wyrażenia stanowiące zawartość funkcji (ang. ).

(defun square (x) (* x x))

Definicja funkcji może zawierać deklaracje, które dostarczają kompilatorowi informacji o ustawieniach optymalizacji lub typach danych argumentów. Może także zawierać dane do dokumentacji, z których Lisp może generować interaktywną dokumentację:

(defun square (x) (declare (integer x) (optimize (speed 3) (debug 0) (safety 1))) "Oblicza kwadrat liczby podanej w argumencie x" (* x x))

Anonimowe funkcje są definiowane przy użyciu lambda-wyrażeń. Styl programowania w Lispie często wykorzystuje anonimowe funkcje jako argumenty do innych funkcji.

Jest wiele operatorów związanych z definiowaniem i manipulowaniem funkcjami. Na przykład funkcja może zostać skompilowana za pomocą funkcji compile. Część implementacji CL uruchamia funkcje w interpreterze, jeżeli nie wydano instrukcji kompilacji, inne domyślnie kompilują w locie.

Przestrzeń nazw funkcji Przestrzeń nazw funkcji jest oddzielona od przestrzeni zmiennych, co jest główną różnicą między CL a Scheme. Do operatorów definiujących nazwy w przestrzeni funkcji należą m.in. defun, flet i labels.

Aby poprzez nazwę przekazać funkcję jako argument do innej funkcji, należy użyć specjalnego operatora function, zwykle skracanego do #'. Pierwszy przykład użycia funkcji sort (powyżej) odwołuje się do funkcji poprzez symbol > za pomocą #'>.

Model wartościowania Scheme jest prostszy – jest tylko jedna przestrzeń nazw i wszystkie elementy formy są wartościowane w dowolnej kolejności – nie tylko argumenty. Przez to kod napisany w jednym dialekcie może być mylący dla programisty bardziej doświadczonego w innym. Na przykład wielu programistów CL używa nazw opisujących zmienną (np. list czy string), co może powodować problemy poprzez lokalne przysłanianie nazw funkcji. By wywołać funkcję znajdującą się w przestrzeni wartości, konieczne jest użycie funkcji funcall czy apply - złożone formyZ wyjątkiem typu (cons (eql lambda) t) jako pierwszy element ewaluowanej listy są bezwarunkowo niedozwolone. Pociągnęło to za sobą również pozornie nie związane odmienności w kulturach Scheme i Common Lispu - przykładowo, w CL-u klasy znajdują się w osobnej „przestrzeni nazw” z interfejsem find-class, zaś w Scheme dodawane do globalnego środowiska jak np. w systemie obiektowym Meroon.

Pytanie, czy oddzielna przestrzeń nazw dla funkcji jest zaletą, czy nie, jest źródłem sporów w społeczności Lispu. Jest to zwykle nazywane debatą Lisp-1 vs. Lisp-2. Nazwy te zostały wymyślone w artykule z 1988 r. przez Richarda Gabriela i Kenta PitmanaNazwy Lisp-1 i Lisp-2 zostały wybrane, by odizolować koncepcję wielu przestrzeni wartości desygnowanych symbolem od różnic ideologicznych obozów Lispu i Scheme, które zwykły wywoływać wielkie emocje podczas konfrontacji obu obozów., którzy porównują w nim obydwa podejściaTechnical Issues of Separation in Function Cells and Value Cells.

Inne typy danych Do pozostałych typów w CL zaliczają się: Ścieżki (pathnames) reprezentują pliki w systemie plików. Ścieżki w CL są bardziej ogólne niż zasady nazewnictwa w większości systemów plików, co sprawia, że dostęp do plików w różnych systemach operacyjnych z poziomu Lispu jest zwykle identyczny. Strumienie (streams) wejścia i wyjścia reprezentują źródła danych binarnych lub tekstowych, takich jak np. terminal czy otwarte pliki.

Condition to typ reprezentujący błąd, wyjątek czy inne zdarzenie, na które program może odpowiedzieć.

Makra Makra w użyciu przypominają funkcje. W odróżnieniu od funkcji, gdzie przekazane wyrażenia zostają obliczane, a tylko wyniki tych wyrażeń przekazane do funkcji, do makr przekazywane są wyrażenia w całości. Makra reprezentują transformację kodu źródłowego, ponieważ zwracają one nowy kod w postaci listy s-wyrażeń, które dopiero potem zostają wywołane. W skrócie można powiedzieć, że makra dostają kod w postaci danych (s-wyrażeń) i zwracają inny kod, który następnie zostaje obliczony.

Makra pozwalają programistom tworzyć nowe składniowe formy języka. Na przykład, makro działające jak until z Perla można napisać tak:

(defmacro until (test &body body) `(do () (,test) ,@body))

;; przyklad (until (= (random 10) 0) (write-line "Hello"))