Haskell, Lisp i gadatliwość [zamknięte]

104

Dla tych z Was, którzy mają doświadczenie zarówno w Haskell, jak i niektórych odmianach Lispa, jestem ciekaw, jak „przyjemnie” (używając okropnego terminu) jest pisanie kodu w Haskell vs. Lisp.

Trochę tła: uczę się teraz Haskella, wcześniej pracowałem z Scheme i CL (i trochę wypad do Clojure). Tradycyjnie można by mnie uznać za fana języków dynamicznych ze względu na ich zwięzłość i szybkość. Szybko zakochałem się w makrach Lispa, ponieważ dało mi to kolejny sposób na uniknięcie gadatliwości i schematu.

Uważam, że Haskell jest niesamowicie interesujący, ponieważ wprowadza mnie w sposoby kodowania, o których istnieniu nie wiedziałem. Zdecydowanie ma pewne aspekty, które wydają się pomagać w osiągnięciu zwinności, takie jak łatwość pisania funkcji częściowych. Trochę martwię się jednak utratą makr Lispa (zakładam, że je zgubiłem; prawdę mówiąc, być może jeszcze się o nich nie dowiedziałem?) I statycznym systemem pisania.

Czy ktokolwiek, kto wykonał przyzwoitą ilość kodowania w obu światach, miałby ochotę skomentować, jak różnią się doświadczenia, które wolisz, i czy ta preferencja jest sytuacyjna?

J Cooper
źródło

Odpowiedzi:

69

Krótka odpowiedź:

  • prawie wszystko, co możesz zrobić z makrami, które możesz zrobić za pomocą funkcji wyższego rzędu (i włączam monady, strzałki itp.), Ale może to wymagać więcej przemyśleń (ale tylko za pierwszym razem i jest fajnie i będziesz lepszy programista), i
  • system statyczny jest na tyle ogólny, że nigdy nie przeszkadza, i, co nieco zaskakujące, faktycznie „pomaga w osiągnięciu zwinności” (jak powiedziałeś), ponieważ podczas kompilacji programu możesz być prawie pewien, że jest poprawny, więc ta pewność pozwala ci spróbować rzeczy, których inaczej bałeś się spróbować - programowanie ma "dynamiczny" charakter, chociaż nie jest to to samo, co w przypadku Lispa.

[Uwaga: istnieje „ Template Haskell ”, który pozwala pisać makra tak jak w Lisp, ale ściśle mówiąc, nigdy nie powinieneś go potrzebować .]

ShreevatsaR
źródło
15
Z wypowiedzi Conora McBride'a, cytowanego przez Don Stewarta: „Lubię myśleć o typach jako o wypaczaniu naszej grawitacji, tak aby kierunek, w którym musimy podróżować [aby pisać poprawne programy] stał się„ w dół ”. System typów sprawia, że ​​pisanie poprawnych programów jest zaskakująco łatwe… zobacz ten post i jego ponowne udostępnienie.
ShreevatsaR
3
Funkcje wysokiego rzędu nie mogą zastąpić makr, aw rzeczywistości CL ma oba z jakiegoś powodu. Prawdziwą siłą makr w CL jest to, że pozwalają programistom na wprowadzenie nowych funkcji językowych, które pomagają lepiej wyrazić rozwiązanie problemu, bez konieczności czekania na nową wersję języka, jak w Haskell lub Java. Na przykład, gdyby Haskell miał taką moc, autorzy Haskell nie musieliby pisać rozszerzeń GHC, czy mogliby być w dowolnym momencie zaimplementowani przez samych programistów jako makra.
mljrg
@mljrg Czy masz konkretny przykład? Zobacz komentarze do odpowiedzi Hibou57 poniżej, gdzie rzekomy przykład okazał się wątpliwy. Chciałbym wiedzieć, co masz na myśli (np. Kod Haskella z makrami i bez nich).
ShreevatsaR
4
Wyjmij curry od Haskella. Czy możesz to zaimplementować z tym, co zostanie w Haskell? Inny przykład: załóżmy, że Haskell nie obsługuje dopasowywania wzorców, czy mógłbyś dodać to samodzielnie, bez konieczności wspierania programistów GHC? W CL możesz dowolnie rozszerzać język za pomocą makr. Przypuszczam, że dlatego CL język nie zmienił się od czasu jego standardu w latach 90-tych, podczas gdy Haskell wydaje się mieć niekończący się strumień rozszerzeń w GHC.
mljrg
64

Przede wszystkim nie martw się o utratę niektórych funkcji, takich jak dynamiczne pisanie. Ponieważ znasz Common Lisp, niezwykle dobrze zaprojektowany język, zakładam, że wiesz, że języka nie można zredukować do zestawu funkcji. Chodzi o spójną całość, prawda?

Pod tym względem Haskell świeci równie jasno jak Common Lisp. Jego funkcje razem zapewniają sposób programowania, który sprawia, że ​​kod jest niezwykle krótki i elegancki. Brak makr jest nieco łagodzony przez bardziej wyszukane (ale również trudniejsze do zrozumienia i użycia) pojęcia, takie jak monady i strzały. System typów statycznych zwiększa twoją moc, a nie przeszkadza, jak ma to miejsce w większości języków zorientowanych obiektowo.

Z drugiej strony programowanie w Haskell jest znacznie mniej interaktywne niż Lisp, a olbrzymia ilość refleksji obecna w językach takich jak Lisp po prostu nie pasuje do statycznego obrazu świata, który zakłada Haskell. W związku z tym dostępne zestawy narzędzi różnią się znacznie między tymi dwoma językami, ale trudno je porównać.

Osobiście wolę ogólnie programować Lisp, ponieważ uważam, że pasuje do sposobu, w jaki lepiej pracuję. Nie oznacza to jednak, że ty też to zrobisz.

Matthias Benkard
źródło
4
Czy mógłbyś rozwinąć trochę więcej na temat „programowania w Haskellu jest znacznie mniej interaktywne”. Czy GHCi naprawdę nie zapewnia wszystkiego, czego potrzebujesz?
Johannes Gerer,
3
@JohannesGerer: Nie próbowałem tego, ale o ile przeczytałem, GHCi nie jest powłoką w działającym obrazie, gdzie można przedefiniować i rozszerzyć dowolne części całego programu podczas jego działania. Ponadto składnia Haskella znacznie utrudnia programowe kopiowanie fragmentów programu między repl a edytorem.
Svante
13

W Haskellu jest mniejsze zapotrzebowanie na metaprogramowanie niż w Common Lisp, ponieważ wiele można zbudować wokół monad, a dodana składnia sprawia, że ​​osadzone DSL wyglądają mniej drzewiasto, ale zawsze jest Template Haskell, jak wspomniał ShreevatsaR , a nawet Liskell (semantyka Haskella + Lisp składnia), jeśli lubisz nawiasy.

Herrmann
źródło
1
łącze Liskell nie działa, ale obecnie jest Hackett .
Will Ness,
10

Odnośnie makr, oto strona, która o tym mówi: Hello Haskell, Goodbye Lisp . Wyjaśnia punkt widzenia, w którym makra po prostu nie są potrzebne w Haskell. Zawiera krótki przykład do porównania.

Przykładowy przypadek, w którym makro LISP jest wymagane, aby uniknąć oceny obu argumentów:

(defmacro doif (x y) `(if ,x ,y))

Przykładowy przypadek, w którym Haskell nie ocenia systematycznie obu argumentów, bez potrzeby stosowania czegoś w rodzaju makrodefinicji:

doif x y = if x then (Just y) else Nothing

I voilà

Hibou57
źródło
17
To powszechne nieporozumienie. Tak, w Haskell lenistwo oznacza, że ​​nie potrzebujesz makr, jeśli chcesz uniknąć oceny niektórych części wyrażenia, ale są to tylko najbardziej trywialny podzbiór wszystkich zastosowań makr. Google dla „The Swine Before Perl” za rozmowę demonstrującą makro, którego nie można zrobić z lenistwem. Ponadto, jeśli chcesz , aby coś było ścisłe, nie możesz tego zrobić jako funkcji - odzwierciedlając fakt, że Schematy delaynie mogą być funkcją.
Eli Barzilay,
8
@Eli Barzilay: Nie uważam tego przykładu za zbyt przekonujący. Oto kompletne, proste tłumaczenie slajdu 40 przez Haskella: pastebin.com/8rFYwTrE
Reid Barton
5
@Eli Barzilay: W ogóle nie rozumiem twojej odpowiedzi. accept to (E) DSL. acceptFunkcja jest analogiem makro przedstawionym na poprzednich stronach, a definicja vjest dokładnie równoległa do definicji vna schemacie na slajdzie 40. Funkcje Haskell i Scheme obliczyć samo z tej samej strategii oceny. W najlepszym przypadku makro pozwala pokazać optymalizatorowi większą część struktury programu. Trudno jest twierdzić, że jest to przykład, w którym makra zwiększają moc ekspresji języka w sposób, który nie jest replikowany przez leniwą ocenę.
Reid Barton,
5
@Eli Barzilay: W hipotetycznym leniwym schemacie możesz napisać to: pastebin.com/TN3F8VVE Moje ogólne twierdzenie jest takie, że to makro kupuje bardzo mało: nieco inna składnia i łatwiejszy czas dla optymalizatora (ale nie miałoby to znaczenia „wystarczająco inteligentny kompilator”). W zamian uwięziłeś się w niewyrażalnym języku; jak zdefiniować automat, który pasuje do dowolnej litery, nie wymieniając ich wszystkich? Nie wiem też, co masz na myśli, mówiąc o „używaniu go na wszystkich listach podrzędnych” lub „wymaganym użyciu miejsca z własnym zakresem”.
Reid Barton
15
Ok, poddaję się. Widocznie twoja definicja DSL jest „argumenty do makro”, a więc mój leniwy przykład Schemat nie jest DSL, mimo że składniowo izomorficzna z oryginałem ( automatoncoraz letrec, :coraz accept, ->coraz nic w tej wersji). Cokolwiek.
Reid Barton
9

Jestem programistą Common Lisp.

Po wypróbowaniu Haskella jakiś czas temu moim osobistym celem było trzymanie się CL.

Powody:

  • dynamiczne pisanie (sprawdź Typy dynamiczne vs. statyczne - analiza oparta na wzorcach autorstwa Pascala Costanzy )
  • argumenty opcjonalne i słowa kluczowe
  • jednolita składnia listy homoikonicznej z makrami
  • składnia przedrostków (nie trzeba pamiętać reguł pierwszeństwa)
  • nieczyste i przez to bardziej odpowiednie do szybkiego prototypowania
  • potężny system obiektowy z protokołem meta-obiektowym
  • dojrzały standard
  • szeroka gama kompilatorów

Haskell ma oczywiście swoje zalety i robi pewne rzeczy w zasadniczo inny sposób, ale po prostu nie daje mi to na dłuższą metę.

Leslie P. Polzer
źródło
Hej, czy masz tytuł tego artykułu Costanza, z którym się połączyłeś? Wygląda na to, że ten plik został przeniesiony.
michiakig
2
Zauważ, że haskell również obsługuje składnię prefiksów, ale powiedziałbym, że monada >> = byłoby bardzo brzydkie przy jego użyciu. Nie zgadzam się również z tym, że nieczystość jest błogosławieństwem: P
alternatywa
2
Podoba mi się ta uwaga dodatkowa: nie zebraliśmy jeszcze danych empirycznych, czy ten problem powoduje poważne problemy w rzeczywistych programach.
Jerome Baum
22
Żaden z przykładów w tym artykule (Pascal Costanza, Typowanie dynamiczne vs. statyczne - Analiza oparta na wzorcach ) nie dotyczy Haskella. Wszystkie są specyficzne dla Javy (a dokładniej, specyficzne dla „programowania zorientowanego obiektowo” ) i nie widzę żadnego z tych problemów w Haskellu. Podobnie, wszystkie inne argumenty są dyskusyjne: można równie dobrze powiedzieć, że Haskell jest „czysty i przez to lepiej nadaje się do szybkiego tworzenia prototypów”, składnia przedrostka nie jest obowiązkowa, że ​​nie ma szerokiego zakresu kompilatorów, które robią różne rzeczy itp.
ShreevatsaR
11
Ten artykuł jest rzeczywiście prawie całkowicie nieistotny dla Haskella. „ dilbert = dogbert.hire(dilbert);” ?? Wątpię, aby wielu programistów Haskell mogło to czytać bez najmniejszego drgania.
lewej około
6

W Haskell możesz zdefiniować funkcję if, co jest niemożliwe w LISP-ie. Jest to możliwe dzięki lenistwu, które pozwala na większą modułowość w programach. Ten klasyczny artykuł Johna Hughesa : Dlaczego FP ma znaczenie , wyjaśnia, w jaki sposób lenistwo wzmacnia kompozycję.

luntain
źródło
5
Schemat (jeden z dwóch głównych dialektów LISP) faktycznie ma leniwą ocenę, chociaż nie jest domyślny, jak w Haskell.
Randy Voet
7
(defmacro doif (xy) `(if, x, y))
Joshua Cheek
4
Makro to nie to samo co funkcja - makra nie działają dobrze z funkcjami wyższego rzędu, takimi jak foldna przykład funkcje nieścisłe .
Tikhon Jelvis
5

Są naprawdę fajne rzeczy, które możesz osiągnąć w Lisp za pomocą makr, które są uciążliwe (jeśli to możliwe) w Haskell. Weźmy na przykład makro „memoize” (patrz Rozdział 9 PAIP Petera Norviga). Dzięki niemu możesz zdefiniować funkcję, powiedzmy foo, a następnie po prostu ocenić (memoize 'foo), co zastępuje globalną definicję foo wersją zapamiętaną. Czy możesz osiągnąć ten sam efekt w Haskell z funkcjami wyższego rzędu?


źródło
3
Niezupełnie (AFAIK), ale możesz zrobić coś podobnego, modyfikując funkcję (zakładając, że jest ona rekurencyjna), tak aby funkcja wywoływała rekursywnie jako parametr (!), Zamiast po prostu wywoływać samą siebie po imieniu: haskell.org/haskellwiki/Memoization
j_random_hacker
6
Możesz dodać foo do leniwej struktury danych, gdzie wartość będzie przechowywana po obliczeniu. To będzie faktycznie to samo.
Theo Belaire
Wszystko w Haskell jest zapamiętywane i prawdopodobnie domyślnie wstawiane w razie potrzeby przez kompilator Haskell.
aoeu256
4

Kontynuując moją przygodę z nauką Haskella, wydaje się, że jedyną rzeczą, która pomaga w „zastępowaniu” makr, jest możliwość definiowania własnych operatorów wrostków oraz dostosowywania ich pierwszeństwa i asocjatywności. Trochę skomplikowany, ale ciekawy system!

J Cooper
źródło