Schemat vs Common Lisp: Które cechy zmieniły Twój projekt? [Zamknięte]

155

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.

SuperElectric
źródło
25
Doskonałe, dobrze sformułowane pytanie. Jestem tym ciekawy; mam nadzieję, że są ludzie, którzy znają oba języki i chętnie udzielą informacji.
Robert Harvey
1
@Josh K - odpowiedź jest jednoznaczna, ale niekoniecznie istnieje jedna ostateczna odpowiedź. Tyle że założę się, że będzie taki, ponieważ ktoś wyjdzie z odpowiedzią, która jest tak niesamowita, że ​​wszyscy są jak whoa!
glenatron
4
@Josh: Być może nie znasz Scheme i Common Lisp. Oba języki same w sobie są bardzo potężne, ale żaden z nich nie jest akceptowany przez główny nurt. Dlaczego to? Być może dlatego, że jest tyle dialektów; który wybierasz? Porównanie tego rodzaju może być bardzo pouczające, a PO dokładnie sformułował pytanie, aby ograniczyć zakres do odpowiedzi, które moim zdaniem są bardzo szczegółowe i możliwe do udzielenia odpowiedzi.
Robert Harvey
13
Ludzie, nie zamykajcie pytania tylko dlatego, że wam się nie podoba lub nie możecie się z nim odnosić. To wyraźnie „prawdziwe” pytanie; jeśli nie możesz znaleźć lepszego powodu, aby go zamknąć, nie powinieneś głosować na zamknięcie.
Robert Harvey
4
Możesz wysłać wiadomość e-mail do Richarda Stallmana z prośbą o odpowiedź.
wassimans

Odpowiedzi:

100

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:

Każdy wystarczająco skomplikowany program C lub Fortran zawiera ad hoc, nieformalnie określone, wolne od błędów, powolne wdrożenie połowy Common Lisp.

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ę.

Michael Lenaghan
źródło
1
Wow, to musiał być naprawdę okropny kod Delphi, jeśli w jakiś sposób udało mu się wykonać 3-6x wolniej niż implementacja Lisp! :(
Mason Wheeler
2
+1: Najbardziej interesującą rzeczą w tym poście jest to, że przeszedłeś z Lisp na Scheme po ukończeniu dużego projektu w Lisp. (A może po prostu czaiłem się na comp.lang.lisp za długo.)
Larry Coleman
25
„Wow, to musiał być naprawdę okropny kod Delphi, jeśli udało mu się jakoś wykonać 3-6x wolniej niż implementacja Lisp!” Tak, liczę to jako moją porażkę, ponieważ nie wyjaśniłem tego lepiej. Implementacja Lisp była w stanie przekształcić wyrażenia użytkownika w wyrażenia Lisp - niezwykle prosty proces - a następnie skompilować wyrażenia Lisp do kodu natywnego (z pełną optymalizacją). Takie jest znaczenie dziesiątej reguły Greenspun.
Michael Lenaghan
1
Fantastyczna odpowiedź! Wybiorę to, przynajmniej dopóki nie pojawi się lepsze :) Jedno pytanie: mówisz, że zdecydowałeś się na Chez Scheme w oparciu o stan pola „dawno temu”. Czy możesz podać rok?
SuperElectric
11
To, że implementacja LISP może swobodnie kompilować coś do kodu maszynowego, zamiast polegać na tłumaczu, jest subtelna i bardzo przydatna. Książka „Let Over Lambda” podkreśla, że ​​właśnie dlatego przenośny pakiet wyrażeń regularnych Common LISP, który klonuje składnię wyrażeń regularnych PERL, znacznie przewyższa PERL. PERL z boku ma interpreter wyrażeń regularnych. Wspólny pakiet LISP kompiluje wyrażenia regularne do kodu.
John R. Strohm,
37

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:

  1. ISTNIENIE : Oba sepleny pojawiły się po kilku innych seplenieniach. Program obrał minimalną, aksomatyczną drogę. CL wybrał barokową trasę.
  2. PRZYPADEK : Zazwyczaj w programie rozróżniana jest wielkość liter. CL nie jest (choć może być). Czasem tego brakuje, ale jego praktyczność jest dyskutowana (przeze mnie).
  3. NAZWY : Nazwy symboli w CL są wielokrotnie dziwne i mylące. TERPRI, PROGNitp. Schemat zwykle ma bardzo rozsądne nazwy. Tego brakuje w CL.
  4. FUNKCJE : CL ma osobną przestrzeń nazw funkcji. To nie brakowało na schemacie. Posiadanie pojedynczej przestrzeni nazw zwykle pozwala na bardzo czyste funkcjonalne programowanie, co często jest trudne lub niezręczne w CL. Ale wiąże się to z kosztem - czasami trzeba zaciemniać nazwy takie jak „ list” do „ lst” w schemacie.
  5. MAKRO : Najbardziej brakuje mi brudnych makr niskiego poziomu w Schemacie. Tak, syntax-ruleswszystko 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.
  6. PRZENOŚNOŚĆ : Często zdarza się, że CL jest bardziej przenośny, mimo że oba języki są znormalizowane. CL jest większy, a zatem istnieje więcej standardowych funkcji do użycia bez bibliotek zewnętrznych. Oznacza to również, że możliwe jest przenośne wykonywanie zadań zależnych od implementacji. Ponadto Schemat cierpi z powodu trylionów implementacji, z których większość jest nieco niezgodna. To sprawia, że ​​CL jest bardzo pożądany.
  7. BIBLIOTEKI : Bardzo związane z moją ostatnią uwagą. Program ma SRFI, ale nie są powszechnie uznawane. Nie ma przenośnego sposobu pracy z bibliotekami. Z drugiej strony CL ma sposoby. A Quicklisp to dar od boga (Xacha) - rodzaj repozytorium bibliotek do użytku.
  8. REALIZACJE : Program cierpi z powodu tak wielu wdrożeń. Nie ma rzeczywistej implementacji kanonicznej. Z drugiej strony CL ma kilka bardzo ładnych implementacji o wysokiej wydajności lub specyficznych zastosowaniach (wysoka wydajność: SBCL, reklama: Allegro, osadzona: ECL, przenośna: CLISP, Java: ABCL, ...).

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.]

Ćwiartka
źródło
co powiesz na (naprawdę) krótkie podsumowanie zwiastuna? ^^
Dave O.
2
Podkreśl najważniejsze informacje. Odpowiedzi powinny być samodzielne.
1
@Dave O. i @ Thorbjørn Ravn Andersen: Dodano podsumowanie zgodnie z żądaniem. Dzięki.
Quadrescence
2
„Barokowa trasa”! Co za doskonały sposób to ująć.
Mark C
W Common Lisp rozróżniana jest wielkość liter, ale konwertuje dane wejściowe na wielkie litery przed ich oceną. Możesz uzyskać małe litery w symbolach, cytując je. Problem z imieniem wynika z tego, że Scheme pozbył się starych, złych nazwisk, a CL nie.
David Thornley,
25

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+(i Alt+)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.

Larry Coleman
źródło
Dzięki za szczegóły! Czy rozumiem cię poprawnie, że myślałeś, że FFI SBCL jest łatwiejszy w użyciu niż Clojure? Jeśli tak, byłbym bardzo zaskoczony, biorąc pod uwagę, że możesz wywoływać metody Java bezpośrednio z Clojure bez konieczności ich pakowania. (A może
musiałeś
6
@SuperElectric: Wywoływanie „wbudowanych” metod Java z Clojure jest trywialne; wywoływanie metod Java znajdujących się w pobranej bibliotece: nie tyle. Naprawdę spędziłem więcej czasu na prawidłowym zapisaniu ścieżki klas i zaimportowaniu wierszy, niż zajęło mi przygotowanie mojej pierwszej metody C z SBCL z CFFI. Ale nie jestem ekspertem od Java, więc Twój przebieg może się różnić.
Larry Coleman,
21

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.

Gangster
źródło
2
Sam tego nie próbowałem, ale widziałem posty na blogach od osób, które korzystają z programu do rakiet wiersza poleceń w Emacsie. Na przykład: bc.tech.coop/scheme/scheme-emacs.htm
Larry Coleman
5
Szczerze mówiąc, brzmi to tak, jakbyś przyszedł do Schematu, chcąc napisać CL zamiast próbować podejść do rzeczy z idiomatycznego POV schematu. Na przykład, czy schemat nie zachęca do ponownego uruchomienia, zamiast używania pętli?
Sanki
@ArtB Schemat nie tylko zachęca do rekursji, ale wymaga jej, więc oczywiście wspomniany wyżej projekt wykorzystał wiele rekurencji. I to właśnie służyło do dodawania powtórzeń (na przykład musisz dołączyć kopię wywołania rekurencyjnego w każdej gałęzi condformularza) 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.
Wyrzuć konto
Jeśli powtarzasz kod przez COND, czy mówisz, że potrzebujesz tylko innej funkcji?
Sanki
@ArtB Funkcja wywołująca funkcję pętli z różnymi argumentami? To byłoby trochę bezcelowe. Widzisz tego rodzaju powtórzenia w prawie każdym kodzie Schematu. Istnieją nawet przykłady w kodzie źródłowym rakiety.
Wyrzuć konto
5

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.

Logika SK
źródło
1
Cześć. Właśnie zacząłem moczyć stopy w Scheme, ostatnio używając rakiety. Czy mógłbyś podać szybki przykład użycia niehigienicznych makr w Racket? Rodzaj dostępnych makr wydaje się być jednym z najczęściej dyskutowanych punktów między CL a Scheme.
orange80
@ orange80, jedynym podejściem jest użycie docs.racket-lang.org/mzlib/mzlib_defmacro.html I oczywiście w trybie R6RS jest mniej restrykcyjny sposób.
SK-logic
@ SK-logic, co robisz z tak niehigienicznymi makrami?
Sled
1
@ArtB, wdrażam eDSLs jako funkcje kompilatora, które mogą sporo zdziałać ze swoim źródłem AST. W ramach takiego podejścia higiena stanowi całkowitą uciążliwość. Możesz zobaczyć, jak to działa: github.com/combinatorylogic/mbase
SK-logic