Nie brakuje niejasnych pytań „Schemat kontra wspólna Lisp” zarówno na StackOverflow, jak i na tej stronie, więc chcę bardziej skoncentrować się na tym. Pytanie dotyczy osób, które napisały w obu językach:
Jakie konkretne elementy doświadczenia w kodowaniu Common Lisp podczas pisania w Scheme najbardziej Ci brakowały? Lub odwrotnie, podczas kodowania w Common Lisp, czego przegapiłeś od kodowania w Scheme?
Niekoniecznie mam na myśli tylko funkcje językowe. Jeśli chodzi o pytanie, wszystkie ważne rzeczy do przeoczenia:
- Określone biblioteki.
- Specyficzne cechy środowisk programistycznych, takich jak SLIME, DrRacket itp.
- Funkcje poszczególnych implementacji, takie jak zdolność Gambit do pisania bloków kodu C bezpośrednio w źródle Scheme.
- I oczywiście funkcje językowe.
Przykłady tego rodzaju odpowiedzi, na które mam nadzieję:
- „Próbowałem wdrożyć X w Common Lisp, a gdybym miał kontynuacje Scheme pierwszej klasy, całkowicie zrobiłbym Y, ale zamiast tego musiałem wykonać Z, co było bardziej bolesne.”
- „Skryptowanie procesu kompilacji w moim projekcie Scheme stawało się coraz bardziej bolesne, gdy rosło moje drzewo źródłowe i łączyłem się w coraz większej liczbie bibliotek C. W następnym projekcie wróciłem do Common Lisp”.
- „Mam dużą bazę kodu C ++ i dla mnie możliwość osadzania wywołań C ++ bezpośrednio w moim kodzie Gambit Scheme była całkowicie warta wszelkich niedociągnięć, które Scheme może mieć w porównaniu do Common Lisp, w tym nawet braku obsługi SWIG.
Mam więc nadzieję na historie wojenne, a nie ogólne sentymenty, takie jak „Schemat to prostszy język” itp.
Odpowiedzi:
Mój stopień licencjata był w dziedzinie kognitywistyki i sztucznej inteligencji. Od tego czasu miałem jednodaniowe wprowadzenie do Lisp. Myślałem, że ten język jest interesujący (jak w „eleganckim”), ale tak naprawdę nie zastanawiałem się długo, dopóki nie natknąłem się na dziesiątą zasadę Greenspun znacznie później:
Punkt Greenspun był (częściowo) taki, że wiele złożonych programów ma wbudowanych tłumaczy. Sugerował, że zamiast budowania interpretera na język, lepiej byłoby użyć takiego języka jak Lisp, który ma już wbudowany interpreter (lub kompilator).
W tym czasie pracowałem nad dość dużą aplikacją, która wykonywała obliczenia zdefiniowane przez użytkownika przy użyciu niestandardowego interpretera dla niestandardowego języka. Postanowiłem spróbować przepisać jego rdzeń w Lisp jako eksperyment na dużą skalę.
Zajęło to około sześciu tygodni. Oryginalny kod zawierał ~ 100 000 wierszy Delphi (wariant Pascal). W Lisp zostało to zredukowane do ~ 10.000 linii. Jeszcze bardziej zaskakujący był fakt, że silnik Lisp był 3-6 razy szybszy. I pamiętaj, że było to dzieło neofity z Lisp! Całe to doświadczenie było dla mnie dość odkrywcze; po raz pierwszy zobaczyłem możliwość połączenia wydajności i ekspresji w jednym języku.
Jakiś czas później, kiedy zacząłem pracować nad projektem internetowym, przesłuchałem wiele języków. Włączyłem Lisp i Scheme do miksu. Na koniec wybrałem implementację programu - schemat Cheza . Byłem bardzo zadowolony z wyników.
Projekt internetowy to wysokowydajny „silnik selekcyjny” . Korzystamy ze Schematu na wiele różnych sposobów, od przetwarzania danych, wysyłania zapytań po generowanie stron. W wielu miejscach zaczęliśmy od innego języka, ale ostatecznie przeprowadziliśmy się do programu z powodów, które opiszę poniżej.
Teraz mogę odpowiedzieć na twoje pytanie (przynajmniej częściowo).
Podczas przesłuchania przyjrzeliśmy się różnorodnym implementacjom Lisp i Scheme. Po stronie Lisp przyjrzeliśmy się (sądzę) Allegro CL, CMUCL, SBCL i LispWorks. Po stronie programu obejrzeliśmy (jak sądzę) Bigloo, kurczaka, Cheza, Gambita. (Wybór języka był dawno temu; dlatego jestem trochę zamglony. Mogę wykopać notatki, jeśli to ważne.)
Od samego początku szukaliśmy a) rodzimych wątków oraz b) Linux, Mac i Windows. Te dwa warunki łącznie znokautowały wszystkich oprócz (chyba) Allegro i Cheza - więc aby kontynuować ocenę, musieliśmy poluzować wymóg wielowątkowości.
Zebraliśmy zestaw małych programów i wykorzystaliśmy je do oceny i testowania. To ujawniło wiele problemów. Na przykład: niektóre implementacje miały wady, które uniemożliwiały uruchomienie niektórych testów do ukończenia; niektóre implementacje nie mogły skompilować kodu w czasie wykonywania; niektóre implementacje nie mogły łatwo zintegrować skompilowanego kodu w czasie wykonywania ze wstępnie skompilowanym kodem; niektóre implementacje miały śmieciarze, które były wyraźnie lepsze (lub wyraźnie gorsze) niż inne ”; itp.
Na nasze potrzeby tylko trzy komercyjne wdrożenia - Allegro, Chez i Lispworks - przeszły nasze podstawowe testy. Z trzech tylko Chez przeszedł wszystkie testy z latającymi kolorami. Wtedy myślę, że Lispworks nie miał natywnych wątków na żadnej platformie (myślę, że teraz) i myślę, że Allegro miało tylko natywne wątki na niektórych platformach. Ponadto Allegro miało opłatę licencyjną za „zadzwoń do nas”, co nie bardzo mi się podobało. Uważam, że Lispworks nie pobierał opłaty za uruchomienie, a Chez miał prosty (i bardzo rozsądny) układ (i uruchomił się tylko wtedy, gdy korzystałeś z kompilatora w czasie wykonywania).
Po wygenerowaniu dość znacznych fragmentów kodu zarówno w Lisp, jak i Scheme, oto kilka punktów porównawczych i kontrastowych:
Środowiska Lisp są znacznie bardziej dojrzałe. Dostajesz o wiele więcej za grosze. (Powiedziawszy to, więcej kodu oznacza również więcej błędów).
Środowiska Lisp są znacznie trudniejsze do nauczenia się. Potrzebujesz dużo więcej czasu, aby stać się biegłym; Common Lisp to ogromny język - i to zanim przejdziesz do bibliotek dodanych przez komercyjne implementacje. (Powiedziawszy to, składnia Scheme jest znacznie bardziej subtelna i skomplikowana niż jakakolwiek inna rzecz w Lisp.)
Środowiska Lisp mogą być nieco trudniejsze do wytworzenia plików binarnych. Musisz „potrząsnąć” obrazem, aby usunąć niepotrzebne bity, a jeśli nie wykonasz poprawnie programu podczas tego procesu, możesz później popełnić błędy w czasie wykonywania . Z kolei w Chez kompilujemy plik najwyższego poziomu, który zawiera wszystkie inne potrzebne pliki i gotowe.
Powiedziałem wcześniej, że w wielu miejscach nie zamierzaliśmy używać Scheme. Dlaczego? Mogę wymyślić trzy powody, dla których nie mam pojęcia.
Po pierwsze, nauczyliśmy się ufać Chezowi (i jego twórcy, Cadence). Poprosiliśmy dużo o narzędzie, które konsekwentnie dostarczało. Na przykład Chez miał historycznie niewielką liczbę wad, a jego menedżer pamięci był bardzo, bardzo dobry.
Po drugie, nauczyliśmy się kochać występ, który otrzymaliśmy od Cheza. Używaliśmy czegoś, co przypominało język skryptowy - i uzyskiwaliśmy z tego szybkość natywnego kodu. Dla niektórych rzeczy, które nie miały znaczenia - ale nigdy nie bolało, a czasem bardzo pomogło.
Po trzecie, nauczyliśmy się kochać abstrakcję, którą może zapewnić Schemat. Nawiasem mówiąc, nie mam na myśli tylko makr; Mam na myśli rzeczy takie jak zamknięcia, lambdas, wezwania do ogona itp. Kiedy zaczniesz myśleć w tych terminach, inne języki wydają się raczej ograniczone w porównaniu.
Czy program jest idealny? Nie; to jest kompromis. Po pierwsze, pozwala to indywidualnym programistom na większą efektywność - ale programiści mają trudność z wzajemnym sprawdzaniem kodu, ponieważ brakuje znaków w większości języków (np. Pętli) w schemacie (np. Istnieje milion sposobów na zrobienie tego pętla for). Po drugie, istnieje znacznie mniejsza grupa programistów, z którymi można rozmawiać, wypożyczać, wypożyczać itp.
Podsumowując, myślę, że powiedziałbym: Lisp i Scheme oferują niektóre funkcje, które nie są powszechnie dostępne nigdzie indziej. Ta zdolność jest kompromisem, więc lepiej, aby była to taka, która ma sens w twoim konkretnym przypadku. W naszym przypadku czynniki decydujące o tym, czy wybrać Lisp czy Scheme, miały więcej wspólnego z bardzo podstawowymi funkcjami (obsługa platformy, wątki platformy, kompilacja w czasie wykonywania, licencjonowanie w czasie wykonywania) niż z funkcjami języka lub biblioteki. Ponownie, w naszym przypadku było to również kompromis: dzięki Chez otrzymaliśmy podstawowe funkcje, których chcieliśmy, ale straciliśmy obszerne biblioteki, które miały komercyjne środowiska Lisp.
Powtórzę też: dawno temu patrzyliśmy na różne Lisps i Schematy; od tego czasu wszystkie ewoluowały i poprawiały się.
źródło
Zwykle nie lubię wklejać linku jako odpowiedzi, ale napisałem artykuł na ten temat na blogu. Nie jest to wyczerpujące, ale zawiera kilka głównych punktów.
http://symbo1ics.com/blog/?p=729
Edycja : Oto główne punkty:
TERPRI
,PROGN
itp. Schemat zwykle ma bardzo rozsądne nazwy. Tego brakuje w CL.list
” do „lst
” w schemacie.syntax-rules
wszystko jest w porządku i eleganckie, dopóki nie chcesz naprawdę zhakować niektórych rzeczy. Z drugiej strony makra higieniczne są czasami pomijane w CL. Brak standardowego sposobu ich wykonania oznacza ponowne wynalezienie koła.Chociaż mówiłem tylko w pierwszej osobie nieco wyżej, powinno być jasne, czego brakuje mi, a czego nie.
[Przepraszam, jeśli są zbyt ogólne. Wygląda na to, że potrzebujesz bardziej szczegółowych informacji. W poście jest kilka szczegółów.]
źródło
Niedawno rozpocząłem projekt domowy przy użyciu biblioteki, która ma wersję C i wersję Java. Chciałem użyć Lisp do projektu i spędziłem około miesiąca wahając się między używaniem Common Lisp, Scheme lub Clojure. Mam trochę doświadczenia ze wszystkimi trzema, ale tylko projektami zabawkowymi. Opowiem ci trochę o moich doświadczeniach z każdym z nich, zanim powiem, który wybrałem.
Rakieta PLT ma ładne IDE, które nie tylko pozwala oceniać wyrażenia z edytora, ale także umożliwia wpisywanie nawiasów zamiast parenów, w razie potrzeby przełączając je z powrotem na parens. Rakieta ma również duży zestaw bibliotek z instalacją i jeszcze więcej dostępnych do pobrania. Pomocny jest także wizualny debugger.
Moja implementacja Common Lisp (SBCL) nie ma IDE, ale jest zwyczajowo w implementacjach CL open source do korzystania z Emacsa i SLIME. Ta kombinacja może być bardzo wydajna. Oprócz możliwości oceny wyrażeń podczas wpisywania ich do pliku źródłowego istnieje również REPL, w którym dostępne są wszystkie polecenia edycyjne emacsa, więc kopiowanie kodu może przebiegać sprawnie w obie strony. Nawet obiekty wyświetlane w buforze REPL można kopiować i wklejać.
Alt+(
iAlt+)
skutecznie radzą sobie z dopasowanymi nawiasami i wcięciami.Wszystkie powyższe funkcje Emacsa są również dostępne dla Clojure. Moje doświadczenia edytorskie w Clojure są podobne do Lisp. Interakcja Java działała dobrze i chciałbym zrobić projekt Clojure, gdy dojrzeje.
Udało mi się uzyskać dostęp do biblioteki przy użyciu wszystkich trzech (Common Lisp, Racket i Clojure), ale ostatecznie wybrałem Common Lisp do projektu. Decydującym czynnikiem było to, że FFI było znacznie łatwiejsze w użyciu w Common Lisp. CFFI ma bardzo dobrą instrukcję z przykładowym kodem i szczegółowymi objaśnieniami każdej metody. Byłem w stanie owinąć 20 funkcji C po południu i odtąd nie musiałem dotykać kodu.
Innym czynnikiem było to, że bardziej znam Common Lisp niż Clojure lub R6RS Scheme. Przeczytałem większość książek Practical Common Lisp i Graham i nie mam nic przeciwko Hyperspec. Nie jest to jeszcze zbyt „lispy” kod, ale jestem pewien, że to się zmieni, gdy zdobędę więcej doświadczenia.
źródło
Programuję w CL i Racket.
Zajmuję się tworzeniem strony internetowej w Common Lisp i napisałem pakiet programów wewnętrznych dla mojego poprzedniego pracodawcy w Racket.
Jako wewnętrzny kod wybrałem Racket (wówczas znany jako PLT Scheme), ponieważ pracodawca był sklepem z systemem Windows i nie mogłem zmusić ich do zapłaty za LispWorks. Jedyną dobrą implementacją CL typu open source dla systemu Windows była (i nadal jest) CCL, która wymaga obsługi SSE w procesorze. Tani pracodawca korzystał ze sprzętu z epoki kamienia. Nawet jeśli pracodawca miał przyzwoity sprzęt, jedyną biblioteką GUI w Common Lisp jest McCLIM, który działa tylko na Uniksie. Racket ma dobrą bibliotekę GUI, która działa zarówno na systemach Unix, jak i Windows, co było kluczowe dla sukcesu mojego projektu.
Ponad rok spędziłem na znoszeniu prymitywnego edytora DrRacket. EMACS nie mógł przekształcić wersji GUI Racketa, znanej wówczas jako MrEd, w gorszą wersję programu Windows. Musiałem to zrobić, nie mogąc ocenić wyrażenia kursorem za jednym naciśnięciem klawisza. Zamiast tego musiałem ręcznie wybrać wyrażenie S, skopiować je, kliknąć okno REPL (ponieważ nie ma naciśnięcia klawisza, aby się do niego przełączyć), a następnie wkleić wyrażenie S. Musiałem też zrezygnować z edytora, który mógłby pokazać oczekiwane argumenty funkcji lub makra, którego używałem. DrRacket nie zastąpi SLIME.
Pracodawca używał zastrzeżonej bazy danych ze skomplikowanym interfejsem API XML, który wymagał mnóstwa pozornie niepotrzebnych informacji, aby móc odpowiedzieć na swoją wersję zapytania SELECT. Zdecydowałem się użyć HTMLPrag zarówno do emisji XML do tego interfejsu API, jak i do analizy odpowiedzi. Działa świetnie.
Musiałem nauczyć się nadmiernie skomplikowanego systemu makr typu „przypadek składni” Racketa, aby napisać makro, które pozwoliłoby mi wchodzić w interakcję z nadmiernie skomplikowanym interfejsem API XML, wpisując formularze wyglądające jak SQL. Ta część byłaby o wiele łatwiejsza, gdybym miał do dyspozycji DEFMACRO. Jednak efekt końcowy był nadal bezproblemowy, chociaż osiągnięcie tego wymagało więcej wysiłku.
Ponadto musiałem zrezygnować z makra LOOP Common Lisp. Rakieta zaczęła zapewniać alternatywę dopiero po tym, jak napisałem większość kodu, a alternatywa wciąż jest do kitu w porównaniu do LOOP (mimo że zespół programistów Racketa twierdzi, że jest lepiej - po prostu się mylą). Skończyło się na pisaniu wielu nazwanych formularzy LET, które używały „car” i „cdr” do iteracji list.
Mówiąc o samochodzie i cdr, nic nie jest bardziej frustrujące niż interpretacja przez Scheme (car '()) jako błędu. Wykorzystałem rozróżnianie wielkości liter w Racket i zaimplementowałem CAR i CDR, które mają semantykę Common Lisp. Jednak oddzielenie „() i #f powoduje, że znacznie mniej przydatne jest zwracanie” () jako wartości domyślnej.
Skończyło się również na ponownym wdrożeniu UNWIND-PROTECT i wymyśliłem własny system restartu, aby wypełnić lukę pozostawioną przez Racket. Społeczność Racket musi dowiedzieć się, że ponowne uruchomienie jest bardzo przydatne i łatwe do wdrożenia.
Formularz let-wartości rakiety był zbyt szczegółowy, więc zaimplementowałem MULTIPLE-VALUE-BIND. Było to absolutnie konieczne, ponieważ Racket wymaga, abyś otrzymywał wszystkie generowane wartości, bez względu na to, czy ich używasz, czy nie.
Później próbowałem napisać klienta API eBay XML w Common Lisp, ale okazało się, że nie ma on czegoś takiego jak HTMLPrag. HTMLPrag jest cholernie użyteczny. Skończyło się na tym projekcie w Racket. Eksperymentowałem z narzędziami do programowania piśmiennictwa Racketa, aby odkryć, że jestem jedynym programistą na Ziemi, który uważa, że poprawnie napisany kod jest trudniejszy do edycji niż zwykły kod, lub źle napisane „niepotrzebne komentarze” kodują umiejętności pisania.
Mój nowy projekt jest realizowany w Common Lisp, co było właściwym wyborem, ponieważ społeczność Racket po prostu nie wierzy w równoległość, która jest niezbędna dla tego projektu. Jedyną rzeczą, o której myślałem, że mogłem tęsknić za Rakietą, były kontynuacje. Byłem jednak w stanie zrobić to, czego potrzebowałem, używając restartów i, z perspektywy czasu, prawdopodobnie mogłem to zrobić po prostym zamknięciu.
źródło
cond
formularza) i błędów (czy napisałem wtedy test zakończenia pętli poprawnie?) Nawet dzisiaj mam wrażenie że rakieta jest przeznaczona głównie dla studentów, a nie profesjonalnych programistów. Za każdym razem, gdy słyszę, że ktoś poza mną korzysta z niego, używa on języka podrzędnego „Beginning Student” i to jest na zajęcia.Schemat został zaprojektowany z myślą o osobnej kompilacji. W rezultacie moc jego makr jest często poważnie ograniczona, nawet z rozszerzeniami, które pozwalają na defmacro w stylu Common Lisp zamiast słabego, ograniczającego higieniczny system makro. Nie zawsze jest możliwe zdefiniowanie makra, które definiuje inne makro, przeznaczone do natychmiastowego użycia w następnym wierszu kodu. Taka możliwość jest niezbędna do wdrożenia wydajnych kompilatorów eDSL.
Nie trzeba dodawać, że implementacje Schematu tylko z makrami higienicznymi R5RS są dla mnie ledwo przydatne, ponieważ mojego stylu metaprogramowania nie można odpowiednio przełożyć na higienę.
Na szczęście istnieją implementacje schematu (np. Rakieta), które nie mają tego ograniczenia.
źródło