W którym obszarze makro LISP jest lepsze niż „zdolność” Ruby do tworzenia DSL

21

Jedną z rzeczy, która sprawia, że ​​Ruby świeci, jest możliwość lepszego tworzenia języków specyficznych dla domeny

Chociaż można duplikować te biblioteki w LISP za pomocą makr, myślę, że implementacja Ruby jest bardziej elegancka. Niemniej jednak myślę, że zdarzają się przypadki, że makro LISP może być lepsze niż Ruby, chociaż nie mogłem wymyślić żadnego z nich.

Więc w jakim obszarze makro LISP jest lepsze niż „zdolność” Ruby do tworzenia DSL, jeśli w ogóle?

aktualizacja

Poprosiłem o tym, ponieważ nowoczesne języki programowania są zbliża osobliwość LISP , jak

  • C ma preprocesor ekspansji makr , choć bardzo prymitywny i podatny na błędy
  • C # ma atrybuty, choć jest to tylko do odczytu, ujawnione przez odbicie
  • Python dodał dekorator, który może modyfikować zachowanie funkcji (i klasy dla v 3.0), choć wydaje się dość ograniczony.
  • Ruby TMTOWTDI, który tworzy elegancki DSL, jeśli zastosowano ostrożność, ale w sposób Ruby.

Zastanawiałem się, czy makro LISP ma zastosowanie tylko w szczególnych przypadkach i czy inne funkcje języka programowania są wystarczająco potężne, aby podnieść abstrakcję i sprostać dzisiejszym wyzwaniom związanym z tworzeniem oprogramowania.

Onesimus Bez ograniczeń
źródło
4
Nie jestem do końca pewien, dlaczego są na to bliskie głosy. Myślałem, że tego rodzaju pytanie chcemy ? Interesujące, prowokujące do myślenia, a zakres jest dość dobrze zdefiniowany.
Tim Post
Spróbuj zaimplementować coś takiego w Ruby: meta-alternative.net/pfront.pdf
SK-logic
@Tim Post: Jednym z problemów jest to, że nie jest to łatwe pytanie, chyba że dobrze znasz zarówno Common Lisp, jak i Ruby. Kolejne to dość ignoranckie odniesienia do innych języków, które mogą denerwować purystów: nie ma języka o nazwie C / C ++, a znaczną częścią C ++ jest system szablonów, a sugestia, że ​​system makr Common Lisp ma zastosowanie tylko w szczególnych przypadkach. Zasadniczo jest to dobre pytanie, ale jest źle napisane i trudno na nie odpowiedzieć.
David Thornley,
@David Thornley, zaktualizowałem post, szczególnie w C \ C ++, i kładę nacisk na C. Jeśli chodzi o Common Lisp, czułem, że skoro inne języki programowania mają wyższą abstrakcję, to funkcja makro jest przeznaczona do specjalnego przypadku. Zadałem pytanie, mając nadzieję, że inni pokażą mi, że makro CL nie jest w specjalnym przypadku, ale nadal jest potężną funkcją.
OnesimusUnbound
@OnesimusUnbound: Naprawdę włączyłbym szablony C ++ do twojej listy przykładów języków, ponieważ są one przynajmniej teoretycznie zdolne do dowolnego obliczenia, jakie może wykonać system makr Lisp. Jeśli chodzi o makra Lisp, zdobądź dobry kod Common Lisp (jest tam dużo kodu Lisp dla F / OS) i wyszukaj „defmacro” (możesz przeszukiwać bez rozróżniania wielkości liter). Prawdopodobnie znajdziesz ich wiele. Weź pod uwagę, że makra są trudniejsze niż pisanie i poprawne funkcje, a zdasz sobie sprawę, że muszą one być ogólnie przydatne.
David Thornley,

Odpowiedzi:

23

Przeczytaj o Lisp, a następnie sam zdecyduj.

Podsumowując, Ruby jest lepszy w zapewnianiu wygodnej składni. Ale Lisp wygrywa bez użycia umiejętności tworzenia nowych abstrakcji, a następnie nakładania abstrakcji na abstrakty. Ale musisz zobaczyć Lisp w praktyce, aby zrozumieć ten punkt. Dlatego polecam książkę.

btilly
źródło
1
„Let Over Lambda” obejmuje bardzo podobny grunt. Zalecane również czytanie.
John R. Strohm,
But Lisp wins, hands down, at the ability to create new abstractions, and then to layer abstraction on abstraction.Teraz wiem dlaczego. . .
OnesimusUnbound
Zapewnienie Lispowi jakiejkolwiek składni nie jest niczym wielkim. W rzeczywistości o wiele łatwiej niż w Ruby dzięki makrom czytnika.
SK-logic
1
@ SK-logic: True. Jednak Lispers unikają makr czytelników, ponieważ po zejściu tą trasą nie jest już dłużej Lispem. W rzeczywistości niektóre warianty Lisp, takie jak Clojure, rezerwują makra czytnika dla projektanta języka.
btilly,
14

Funkcje Rubiego do tworzenia DSL nie zmieniają charakteru języka. Funkcje metaprogramowania Ruby są nieodłącznie związane ze składnią i semantyką Ruby, a wszystko, co piszesz, musi zostać przeniesione do modelu obiektowego Ruby.

Porównaj to z Lisp (i Scheme, których funkcje makr różnią się ), gdzie makra działają na samym abstrakcyjnym programie. Ponieważ program Lisp jest wartością Lisp, makro jest funkcją odwzorowującą jedną zasadniczo dowolną składnię na inną.

W rzeczywistości Ruby DSL nadal czuje się jak Ruby, ale DSL Lisp nie musi czuć się jak Lisp.

Jon Purdy
źródło
6
+1: LOOP nie wydaje się bardzo Lispy, jako przykład DSL.
Frank Shearar
2

DSL Ruby wcale nie są DSL i absolutnie nie znoszę ich używać, ponieważ ich dokumentacja kłamie na temat tego, jak naprawdę działają. Weźmy na przykład ActiveRecord. Pozwala „zadeklarować” powiązania między modelami:

class Foo < ActiveRecord::Base
    has_one :bar
    has_one :baz
end

Ale deklaratywność tej „DSL” (podobnie jak deklaratywność classsamej składni Ruby ) jest okropnym kłamstwem, które może zostać ujawnione przez każdego, kto rozumie, jak faktycznie działają „DSL” Ruby:

class Foo < ActiveRecord::Base
    [:bar,:baz,:qux,:quux].each do |table|
        has_one table if i_feel_like_it?(table)
    end
    puts "Just for shits and giggles, and to show"
    puts "just how fucked up Ruby really is, we're gonna ask you"
    puts "which SQL table you want the Foo model to have an"
    puts "association with.\n"
    puts "Type the name of a table here: "
    has_one gets.chomp.to_sym
end

(Po prostu spróbuj zrobić coś zbliżonego do tego w ciele defclassformularza Lisp !)

Gdy tylko w kodzie znajdziesz kod podobny do powyższego, każdy programista w projekcie musi w pełni zrozumieć, w jaki sposób faktycznie działa DSL Ruby (a nie tylko iluzję, którą tworzą), zanim będzie mógł zachować kod. Dostępna dokumentacja będzie całkowicie bezużyteczna, ponieważ dokumentują tylko idiomatyczne użycie, które zachowuje deklaratywną iluzję.

RSpec jest jeszcze gorszy od powyższego, ponieważ ma dziwne przypadki brzegowe, które wymagają obszernej inżynierii wstecznej do debugowania. (Cały dzień próbowałem dowiedzieć się, dlaczego jeden z moich przypadków testowych został pominięty. Okazało się, że RSpec wykonuje wszystkie przypadki testowe, które mają konteksty po przypadkach testowych bez kontekstu, niezależnie od kolejności, w jakiej występują w źródle , ponieważ contextmetoda umieszcza blok w innej strukturze danych niż normalnie.)

Listy DSL Lisp są implementowane przez makra, które są małymi kompilatorami. Listy DSL, które możesz utworzyć w ten sposób, nie są zwykłym nadużyciem istniejącej składni Lisp. Są to prawdziwe mini-języki, które można napisać, aby były całkowicie płynne, ponieważ mogą mieć własną gramatykę. Na przykład LOOPmakro Lispa jest znacznie potężniejsze niż eachmetoda Ruby .

(Wiem, że już zaakceptowałeś odpowiedź, ale nie każdy, kto ją przeczyta, będzie chciał przeczytać całość On Lisp ).

Konto wyrzucenia
źródło