Czy Lisp nadal ma jakąś specjalną funkcję, która NIE została przyjęta przez inne języki programowania?

35

Czy Lisp nadal ma jakąś specjalną funkcję, która NIE została przyjęta przez inne języki programowania?

Przez Lisp mam na myśli wszystkie języki programowania Lisp jako całość. Powiedziano mi, jak niesamowite jest Lisp i wiem, że wiele języków zostało zainspirowanych przez Lisp. Ale czy Lisp nadal ma jakąś ekskluzywną funkcję projektową, której po prostu nie da się zrobić w żadnym innym języku?

Powodem, dla którego zadałem pytanie, jest to, że ostatnio, będąc programistą-amatorem, zacząłem uczyć się Clojure dla zabawy, w wyniku czego znalazłem wiele postów i komentarzy związanych z Lisp, mówiąc tylko jedno: „Lisp jest wyjątkowy ”, ale inne współczesne języki programowania przyjęły już i wykradły wiele pomysłów Lisp, takich jak warunki warunkowe, rekursja i funkcja obywatela pierwszej klasy. A nawet metaprogramowanie może być wykonywane w wielu językach.

Czy coś przeoczyłem i czy „Lisp wciąż jest inny”?

Albo mam szczęście, ponieważ inne współczesne języki ukradły wszystkie dobre części z Lisp, aby nie trzeba było zagłębiać się w nawiasowy świat Lisp , a „Lisp był inny”.

iceX
źródło
3
Udostępnianie badań pomaga wszystkim. Powiedz nam, co próbowałeś i dlaczego nie spełnia twoich potrzeb. To pokazuje, że poświęciłeś trochę czasu, aby spróbować sobie pomóc, oszczędza nam to powtarzania oczywistych odpowiedzi, a przede wszystkim pomaga uzyskać bardziej konkretną i odpowiednią odpowiedź. Zobacz także How to Ask
gnat
3
@gnat Thx po poradę i zaktualizowałem moje pytanie :)
iceX
10
Jednym z problemów jest to, że gdy język ma określony podzbiór funkcji Lisp (na przykład wyrażenia S i makra), ludzie twierdzą, że jest to Lisp. Co oczywiście powoduje, że (według tych osób) żaden język inny niż Lisp nie może mieć tych funkcji.
9
Wydaje mi się, że nie ma innego języka, w którym nawiasy okrągłe są jedyną formą grupowania wszystkiego :-)
Doc Brown
Lisp jako rodzina może nie być strasznie wyjątkowy, ale wiele dialektów seplenienia (Racket, CL, Scheme, Clojure) wciąż oferuje wiele użytecznych / unikalnych funkcji.
Daniel Gratzer

Odpowiedzi:

28

Kanonicznym odniesieniem do tego rodzaju pytań jest What What Lisp Different . Paula Grahama . Dwie pozostałe kluczowe cechy Lisp, które nie są powszechnie dostępne, zgodnie z tym artykułem w momencie jego pisania, to:

8. Notacja kodu przy użyciu drzew symboli.

9. Cały język zawsze dostępny. Nie ma rzeczywistego rozróżnienia między czasem odczytu, czasem kompilacji i środowiskiem uruchomieniowym. Możesz kompilować lub uruchamiać kod podczas odczytu, odczytywać lub uruchamiać kod podczas kompilacji oraz odczytywać lub kompilować kod w czasie wykonywania.

Komentarz dotyczy każdego punktu i podaje popularne języki, w których ta funkcja jest dostępna.

8, co (z 9) sprawia, że ​​makra Lisp są możliwe, jest jak dotąd unikalne dla Lisp, być może dlatego, że (a) wymaga tych parens lub czegoś równie złego, i (b) jeśli dodasz ten końcowy przyrost mocy , nie możesz już twierdzić, że wymyśliłeś nowy język, a jedynie zaprojektować nowy dialekt Lisp; -)

Pamiętaj, że ten artykuł został po raz ostatni poprawiony w 2002 roku, aw ciągu ostatnich 11 lat pojawiło się wiele nowych języków, z których niektóre mogą zawierać wszystkie te funkcje Lisp w swoich projektach.

Greg Hewgill
źródło
2
Te funkcje nie są unikalne w Lisp (i bezpośrednio wyprowadzonych wariantach) i nie były od dłuższego czasu.
Donal Fellows,
21
@iceX: Różnica między JavaScript a takimi językami jak Lisp, Smalltalk, Self, Newspeak, APL, Factor, Forth itd. polega na tym, że w JavaScript programy są „martwe”. Są to pliki tekstowe. Zabijasz działający program, edytujesz plik tekstowy, a następnie uruchamiasz całkowicie nową kopię programu. W tych innych (tak zwanych „żywych” środowiskach) nigdy nie zatrzymujesz działającego programu, sam działający program jest zestawem obiektów w pamięci, którymi manipulujesz tak samo, jak manipulujesz dowolnym innym obiektem podczas jego działania . (Uwaga: nie dotyczy to Clojure, ale większość starszych Lisps robi to w ten sposób.)
Jörg W Mittag
2
@ JörgWMittag Wow ... Tak więc języki programowania, takie jak Clojure, Clojurescript, lispyscript, w rzeczywistości nie są „prawdziwymi seplenieniami”, ponieważ nie mogą zmienić swojego kodu tak, jak stara Lisp, podobnie jak Common Lisp. Ale ... jeśli nie mogą tego zmienić ... po co męczyć się z użyciem wyrażenia S, które moim zdaniem
służyło do
4
@iceX: Wiele języków ma evalpodobne lub podobne, a całkiem sporo może wykonywać metaprogramowanie, np. Szablon Haskell Haskell. (Prawdopodobnie) wyjątkową cechą Lisp jest to, że reprezentacja danych, kodu i meta-kodu jest taka sama - nie tylko w składni, ale tak naprawdę jest taka sama.
tdammers
2
@iceX Eval to tylko jedna jej część, ale nie wszystko. W Lisp możesz wykonać kod w czasie kompilacji. Możesz wykonać kod w czasie odczytu. Tak, clojure obsługuje cały dynamizm Lispa, ma makra, makra czytnika i eval, a także możliwość dołączenia REPL do działającego programu w celu modyfikacji go w locie.
stonemetal
15

Odpowiedź na to pytanie jest trudna, ponieważ ktoś musiałby znać wszystkie języki, aby wiedzieć, że żaden inny nie ma żadnej konkretnej funkcji dostępnej w Lisp, więc poniższe informacje są oparte na językach, z którymi mam doświadczenie.

Poza moją głową warunki są czymś, czego nie widziałem w żadnym innym języku. Pomyśl o „wyjątkach”, ale tam, gdzie stos wywołań nie jest rozwijany i gdzie wywołujący może wysłać wartość odzyskiwania do miejsca wyjątku, ale bez zakłócania stosu wywołań między modułem obsługi i źródłem wyjątku. Szczerze mówiąc, jest to tak naprawdę specjalne zastosowanie kontynuacji, więc Ruby i Scheme (przynajmniej) mogą to zrobić.

Makro system Lisp korzysta z regularności / homoikoniczności, ale Scala planuje wprowadzić je jako stabilną funkcję w 2.12, a szablon Haskell twierdzi, że podobne funkcje. Twierdziłbym, że będą one bardziej złożone pod względem składniowym niż w przypadku Lispa, ale generowanie kodu w czasie kompilacji istnieje niezależnie od tego.

Pomyśl o tym, jednak proste budowanie formularzy jest tylko jednym rodzajem makra dostępnym w Lisp: nigdzie indziej nie widziałem odpowiednika makr kompilatora lub czytnika.

Zdolność niektórych dialektów (np. SBCL ) do zapisania pełnego, wznawianego obrazu procesu jest fajna, ale znowu nie jest wyjątkowa: Smalltalk robi to od dziesięcioleci.

Wiele innych języków zezwala na przypisywanie destrukcji przy zwracaniu tablic, ale wartości # 'i #' wiele-wiązania-wiązania / let-wartości nadal wydają się być specyficzne dla Common Lisp i schematu (który nadal może dokonywać regularnej destrukcji również ). „Niedostrzegalna” Perla pozwala funkcji określić, czy jest wywoływana w kontekście skalarnym, listowym czy pustym, aby mogła dostosować swoją wartość zwracaną w podobny (-ish) sposób, ale nie widziałem „prawdziwych” wielokrotnych wartości zwracanych poza programu / CL.

Jeśli chodzi o funkcje językowe, Lisp prawdopodobnie nie jest w stanie zrobić tego, czego nie potrafią inne języki (Turing jest kompletny). Jest to jednak język, w którym kod jest wyrażany za pomocą własnych struktur danych, dzięki czemu Big Idea ™ - ten kod to dane - jest czymś stosunkowo łatwym w obsłudze.

Danny Woods
źródło
3
Makra Scala są znacznie mniej wydajne niż makra Lisp, a także makra TH, makra higieniczne Schemat itp. Równie potężny system można znaleźć w MetaLua i Converge.
SK-logic
4

Po tylu dziesięcioleciach nie sądzę, żeby było coś wyłącznego dla Lisp. Ale nawet dzisiaj istnieje wiele interesujących rzeczy, które trudno znaleźć poza Lisps. Kilka rzeczy, które przychodzą mi na myśl:

  • Wysokiej jakości system obiektowy z wyrafinowanymi meta-protokołami (tj. CLOS) nie jest zbyt popularny.
  • od czasu do czasu pojawiają się różne metody, ale większość ludzi nigdy o nich nie słyszała.
  • Jak zauważyli inni, system warunków jest dość wyrafinowany w porównaniu do popularnych mechanizmów obsługi wyjątków.
  • Kompaktowa reprezentacja semantyki języka (eval), wzmacniające podejście do definiowania własnego języka i udostępnianie go do prostych głębokich adaptacji (patrz SICP ) - funkcje „ewaluacyjne” popularnych popularnych języków po prostu nie mają tych samych właściwości.

Wreszcie, Lisp może się wiele nauczyć, nie dotyczy samego języka, ale stał się częścią historii Lisp i zagubił się w czasie. Np. Interlisp, Symbolics Genera itp. ... jeśli nigdy nie położysz rąk na Genera, zapoznaj się z tym wątkiem comp.lang.lisp, w którym Kent Pitman opisuje, w jaki sposób „Emacs jest tylko bladym cieniem Zmaców Genery” - wszystko to zostało włączone przez mając potężny system Lisp, którego częścią był Zmacs, który działał na maszynie Lisp.

Thiago Silva
źródło
Nigdy nie widziałem dobrego wyjaśnienia multimetod, które odróżniałyby je od przeciążonych metod , co jest standardową cechą w prawie każdym nowoczesnym języku imperatywnym, z wyjątkiem faktu, że wysyłanie multimetod jest rozwiązywane dynamicznie w czasie wykonywania i dlatego jest znacznie wolniejsze niż stosowanie metod przeciążonych, które są rozwiązywane w czasie kompilacji.
Mason Wheeler
Mają ważne rozróżnienia. Jeśli masz problemy ze znalezieniem zasobów na ten temat, zawsze możesz utworzyć nowe pytanie tutaj.
Thiago Silva
Julia ma multimetody, a parametryczny polimorfizm Haskella przypomina trochę multimetody. Istnieją sposoby symulowania wielu metod w wielu językach. System warunku jest czymś, czego naprawdę chcę szczególnie (edytuj i kontynuuj, chociaż widziałem go dla debuggerów C #).
aoeu256
4

To niekoniecznie pewna pojedyncza funkcja . To cały wygląd i sposób działania oraz sposób, w jaki pewne zestawy funkcji działają razem.

JavaScript lub Java mają wiele funkcji Lisp (maszyna wirtualna, kompilator / ewaluator, odśmiecanie itp.). Ale JavaScript na przykład nie ma symbolicznej części programistycznej, brakuje matematycznych możliwości (wewnętrznie ma tylko zmiennoprzecinkowe), brakuje obsługi błędów i tak dalej.

Wiele systemów Common Lisp jest zoptymalizowanych pod kątem programistycznym, w którym jeden stopniowo rozszerza nowe oprogramowanie, rozszerzając język Lisp w różnych wymiarach przy użyciu różnych technik metaprogramowania - bez konieczności ponownego uruchamiania oprogramowania przez długi czas. Dlatego musi być elastyczny i rozszerzalny - ale jednocześnie musi być solidny. Zmiana języka (makra są po prostu sposobem na rozszerzenie kompilatora) bez zawieszania programu.

Teraz coś takiego jak JavaScript jest także używane do rozszerzenia programu, zwykle przeglądarki internetowej. Ale przez większość czasu nie robi się zbyt wiele metaprogramowania w JavaScript - oprócz niektórych hakerów OOP .

Przykład:

Można zaimplementować ogólne zaawansowane oprogramowanie matematyczne dla dziedziny algebry komputerowej głównie na dwa sposoby: napisać silnik w C ze specjalnym językiem na wierzchu (jak Mathematica ) lub w bardziej zaawansowanym dialekcie Lisp. Macsyma / Maxima in Common Lisp, Reduce in Standard Lisp, Axiom in Common Lisp.

(Istnieje również jeden lub więcej napisanych w Pythonie).

Nie ma wielu systemów, które oferują zestaw funkcji czegoś takiego jak Axiom , który działa na Common Lisp.

To, co sprawiło, że Lisp jest atrakcyjny dla tego typu aplikacji, to połączenie różnych funkcji: zaawansowanych podstawowych matematyki (bignum, współczynniki, ...), obliczeń symbolicznych, interaktywnego kompilatora itp. Całkiem możliwe jest uzyskanie tych rzeczy poprzez wdrożenie ich w nisko- poziom języka. W ten sposób zaimplementujesz 50% lub więcej typowego systemu Lisp.

Rainer Joswig
źródło
2

Nie, o ile mi wiadomo. Forth jest równie dynamiczny jak Lisp, być może tym bardziej, że dynamiczny kod w Forth wygląda jak zwykły kod Forth, podczas gdy makra Lisp mają tendencję do używania innych funkcji niż zwykły kod Lisp ( przynajmniej w Clojure nigdy nie użyłem cudzysłowu składni poza makrem) i w rezultacie wyglądają naprawdę inaczej niż zwykły kod Lisp. Jako przykład tego, jak dynamiczny jest Forth, oto sposób na implementację komentarzy w Forth :

: (   41 word drop ; immediate
( That was the definition for the comment word. )
( Now we can add comments to what we are doing! )
kamieniste
źródło
1
Choć Forth wydaje się być zabawny, zawsze uważałem, że jest beznadziejnie nieprzejrzysty i niewygodny, być może dlatego, że jest oparty na stosie i wszystko jest odwrócone.
Robert Harvey
2
Z ciekawości, co rozumiesz przez „oddzielony od głównej części języka”? Makra w Lisp mają pełny dostęp do wszystkich funkcji zdefiniowanych w punkcie, w którym makro jest zdefiniowane, a każda z nich może zostać użyta do zbudowania formularza, do którego makro się rozwija. Może to obejmować informacje pobrane z bazy danych i / lub serwera. Twój przykład komentarza można zdefiniować jako: (komentarz defmacro (i reszta ciała)), prawda?
Danny Woods
1
@DannyWoods Mam na myśli, że makra nie wyglądają jak zwykły kod. Przynajmniej w clojure można odróżnić kod makr od zwykłego kodu, ponieważ intensywnie używa cudzysłowu składniowego, niecytowanego splicingu itp., Co jest niezwykłe w zwykłym kodzie. Ten czwarty przykład, który podałem, wygląda jak normalny kod, dopóki nie zobaczysz natychmiastowego. Ponowne odczytanie mojej odpowiedzi jest po prostu słabo wyrażone.
stonemetal
1
@stonemetal Nie martw się, ale warto zauważyć, że w cytatach składniowych nie ma nic wspólnego z makrami, ani w Common Lisp, ani w Clojure. Czasami wygodnie jest mieć wyrażenia wsteczne w definicjach funkcji regularnych, a ciała makr można budować ręcznie za pomocą zwykłych list (chociaż musisz to zrobić tylko raz lub dwa razy, aby zdecydować, że cytat składni jest dobry!)
Danny Woods
1
Szczerze mówiąc, to samo można zrobić w Lisp, rejestrując niestandardowe makro czytnika.
fjarri
2

Lisp ma wiele dialektów, a każdy z nich ma swój własny zestaw funkcji. Moją ulubioną funkcją, która prawdopodobnie nie zostanie zaadaptowana w żadnym innym języku, jest „stos spaghetti” firmy Interlisp .

Stos spaghetti jest jak zamknięcie, ale na sterydach. Zapisuje nie tylko bieżącą funkcję, ale cały kontekst do góry stosu. Coś w rodzaju wspólnej procedury , z wyjątkiem tego, że można je utworzyć dowolnie, co daje hierarchię kontekstów stosu.

ddyer
źródło
Nie wiedziałem, że to sprawdzę, dziękuję za odpowiedź :)
iceX
7
Czy to to samo, co kontynuacje Scheme?
Nicola Musatti,
1
@NicolaMusatti, „stos spaghetti” to wspólna strategia implementacji kontynuacji podobnych do schematu (którą można wywołać po powrocie funkcji, która je utworzyła).
Alex D