Leniwa ocena vs makra

Jestem przyzwyczajony do leniwej oceny z Haskell, i czuję się rozdrażniony z eager-by-default języków teraz, gdy używałem leniwej oceny poprawnie. Jest to właściwie dość szkodliwe, ponieważ inne języki, których używam, sprawiają, że leniwie oceniam rzeczy bardzo niewygodne, zwykle związane z rozwijaniem niestandardowych iteratorów i tak dalej. Więc po prostu zdobywając trochę wiedzy, uczyniłem siebie mniej produktywnym w moich oryginalnych językach. Westchnienie.

Ale słyszę, że AST makra oferują inny czysty sposób robienia tego samego. Często słyszałem stwierdzenia typu "leniwa ocena czyni makra zbędnymi" i vice-versa, głównie ze sparingowych społeczności Lisp i Haskell.

Zajmowałem się makrami w różnych wariantach Lispu. Po prostu wydawały się naprawdę zorganizowanym sposobem kopiowania i wklejania fragmentów kodu do obsługi w czasie kompilacji. Z pewnością nie byli Świętym Graalem, o którym Lispers lubi myśleć. Ale to prawie na pewno dlatego, że nie mogę użyć jak należy. Oczywiście, posiadanie systemu makr pracującego na tej samej podstawowej strukturze danych, z którą sam język jest zmontowany, jest naprawdę przydatne,ale nadal jest to zorganizowany sposób kopiowania i wklejania kodu. Przyjmuję do wiadomości, że bazowanie systemu makr na tym samym AST, co język, który pozwala na pełną zmianę runtime jest potężne.

Chcę wiedzieć, jak można używać makr, aby zwięźle i zwięźle robić to, co robi leniwa ocena? Jeśli chcę przetworzyć wiersz pliku po linijce bez szarpania całego, po prostu zwracam listę, która ma rutynę odczytywania linijek. To doskonały przykład DWIM (do what I mean). Nawet nie muszę o tym myśleć.

Najwyraźniej nie rozumiem makr. Używałem ich i nie byłem szczególnie pod wrażeniem, biorąc pod uwagę hype. Coś mi umyka, czego nie rozumiem czytając dokumentację online. Czy ktoś może mi to wszystko wyjaśnić?

Author: acfoltzer, 2011-08-13

5 answers

Leniwa ewaluacja może zastąpić niektóre zastosowania makr (te, które opóźniają ewaluację w tworzeniu konstruktów sterujących), ale converse nie jest tak naprawdę prawdą. Możesz użyć makr, aby uczynić konstrukcje opóźnionej oceny bardziej przejrzystymi -- zobacz SRFI 41 (Streams) dla przykładu jak: http://download.plt-scheme.org/doc/4.1.5/html/srfi-std/srfi-41/srfi-41.html

Na dodatek możesz napisać własne leniwe Io prymitywy.

Z mojego doświadczenia wynika jednak, że leniwy kod w ścisłym języku ma tendencję do wprowadzania narzutu w porównaniu do wszechobecnie leniwego kodu w środowisku uruchomieniowym zaprojektowanym, aby skutecznie wspierać go od samego początku-co, pamiętajcie, jest naprawdę problemem implementacyjnym.

 21
Author: sclv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-12 22:55:21

Leniwa ocena sprawia, że makra są zbędne

To czysty nonsens (nie twoja wina; słyszałem to wcześniej). To prawda, że możesz użyć makr, aby zmienić kolejność, kontekst itp. ewaluacji wyrażeń, ale to jest najbardziej podstawowe użycie makr i naprawdę nie jest wygodne symulowanie leniwego języka za pomocą makr ad-hoc zamiast funkcji. Więc jeśli przyszliście na makra z tego kierunku, rzeczywiście bylibyście rozczarowani.

Makra służą do rozszerzania język z nowymi formami składniowymi. Niektóre z specyficznych możliwości makr to

  1. wpływające na porządek, kontekst itp. oceny ekspresji.
  2. tworzenie nowych formularzy wiążących (tj. wpływających na zakres wyrażenie jest oceniane w).
  3. wykonywanie obliczeń w czasie kompilacji, w tym analizy kodu i transformacji.

Makra, które robią (1) mogą być dość proste. Na przykład w formularzu obsługi wyjątków, with-handlers, jest to tylko makro, które rozszerza się na call-with-exception-handler, niektóre warunki i niektóre kody kontynuacyjne. Używa się go tak:

(with-handlers ([(lambda (e) (exn:fail:network? e))
                 (lambda (e)
                   (printf "network seems to be broken\n")
                   (cleanup))])
  (do-some-network-stuff))

Makro implementuje pojęcie "klauzul predykatu i obsługi w dynamicznym kontekście wyjątku" w oparciu o prymitywne call-with-exception-handler, które obsługuje wszystkie wyjątki w momencie ich wystąpienia.

Bardziej wyrafinowane użycie makr jest implementacją generatora parserówLALR(1) . Zamiast oddzielnego pliku, który wymaga wstępnego przetworzenia, parser forma jest po prostu innym rodzajem wyrażenia. Pobiera opis gramatyki, oblicza tabele w czasie kompilacji i tworzy funkcję parsera. Procedury akcji mają zasięg leksykalny, więc mogą odwoływać się do innych definicji w pliku lub nawet zmiennych związanych z lambda. W procedurach akcji można nawet używać innych rozszerzeń językowych.

Na końcuTyped Racket jest typowanym dialektem Racket zaimplementowanym za pomocą makr. Posiada wyrafinowany system typu zaprojektowany tak, aby pasował idiomy kodu Racket/Scheme, a także współdziałają z nieopisanymi modułami, chroniąc wpisane funkcje za pomocą dynamicznych kontraktów programowych (również zaimplementowanych za pomocą makr). Jest on zaimplementowany przez makro" typed module", które rozszerza, sprawdza i przekształca ciało modułu, a także pomocnicze makra do dołączania informacji o typie do definicji itp.

FWIW, jest też Lazy Racket , leniwy dialekt Racket. Nie jest zaimplementowany przez przekształcenie każdej funkcji w makro, ale autor: rebinding lambda, define, i składnia aplikacji funkcji do makr, które tworzą i wymuszają obietnice.

Podsumowując, leniwa Ocena i makra mają mały punkt przecięcia, ale są bardzo różne rzeczy. A makra z pewnością nie podlegają leniwej ocenie.

 58
Author: Ryan Culpepper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-03 09:23:32

Lenistwo jest denotacyjny, podczas gdy makra nie są. Dokładniej mówiąc, jeśli dodasz nieścisłość do języka denotacyjnego, wynik jest nadal denotacyjny, ale jeśli dodasz makra, wynik nie jest denotacyjny. Innymi słowy, znaczenie wyrażenia w leniwym czystym języku jest funkcją wyłącznie znaczeń wyrażeń składowych; podczas gdy makra mogą dawać semantycznie odrębne wyniki z semantycznie równych argumentów.

W tym sensie makra są bardziej potężny, podczas gdy lenistwo jest odpowiednio lepiej zachowywane semantycznie.

Edit : dokładniej, makra nie są denotacyjne z wyjątkiem w odniesieniu do tożsamości / trywialnego denotacji (gdzie pojęcie "denotacyjne" staje się próżne).

 22
Author: Conal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:09:44

Lisp zaczął się pod koniec lat 50. ubiegłego tysiąclecia. Zobacz Funkcje rekurencyjne wyrażeń symbolicznych i ich obliczenia maszynowe . Makra były Nie częścią tego Lispu. Chodziło o obliczanie za pomocą wyrażeń symbolicznych, które mogą reprezentować wszelkiego rodzaju formuły i programy: wyrażenia matematyczne, wyrażenia logiczne, zdania języka naturalnego, programy komputerowe, ...

Później wymyślono makra Lispu i są one zastosowaniem powyższej idei do Sam Lisp: makra przekształcają wyrażenia Lispu (lub podobne do Lispu) na inne wyrażenia Lispu używając pełnego języka Lispu jako języka transformacji.

Możesz sobie wyobrazić, że z makrami możesz zaimplementować potężne pre-procesory i kompilatory jako użytkownik Lispu.

Typowy dialekt Lispu używa ścisłej oceny argumentów: wszystkie argumenty funkcji są oceniane przed wykonaniem funkcji. Lisp ma również kilka wbudowanych form, które mają różne reguły oceny. IF jest taki przykład. W Common Lispie IF jest tak zwanym operatorem specjalnym.

Ale możemy zdefiniować nowy Lisp-like (sub-) język, który używa leniwej oceny i możemy pisać makra, aby przekształcić ten język w Lisp. Jest to aplikacja dla makr, ale zdecydowanie nie jedyna.

Przykładem (stosunkowo starym) takiego rozszerzenia Lispu, które używa makr do implementacji transformatora kodu dostarczającego struktury danych z leniwą ewaluacją jest rozszerzenie SERIES do Common Lispu.

 9
Author: Rainer Joswig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-13 18:52:20

Makra mogą być używane do obsługi leniwej oceny, ale są tylko jej częścią. Głównym punktem makr jest to, że dzięki nim w zasadzie nic nie jest stałe w języku.

Jeśli programowanie przypomina zabawę klockami LEGO, za pomocą makr można również zmienić kształt klocków lub materiał, z którego są zbudowane.

Makra to coś więcej niż tylko opóźniona ocena. Był on dostępny jako fexpr (prekursor makr w historii Lispu). Makra chodzi o przepisywanie programu, gdzie fexpr jest to tylko specjalna sprawa...

Jako przykład rozważ, że piszę w wolnym czasie mały Lisp do kompilatora javascript i pierwotnie (w jądrze javascript) miałem tylko lambda z obsługą argumentów &rest. Teraz jest wsparcie dla argumentów słów kluczowych, a to dlatego, że redefiniowałem znaczenie lambda w samym Lispie.

Mogę teraz napisać:

(defun foo (x y &key (z 12) w) ...)

I wywołanie funkcji za pomocą

(foo 12 34 :w 56)

Podczas wykonywania tego wywołania, w ciele funkcji parametr w będzie związany do 56, a parametr z do 12, ponieważ nie został przekazany. Wyświetli się również błąd runtime, jeśli do funkcji zostanie przekazany nieobsługiwany argument słowa kluczowego. Mógłbym nawet dodać obsługę sprawdzania w czasie kompilacji przez ponowne zdefiniowanie, co oznacza kompilowanie wyrażeń (np. dodanie sprawdzania, czy "statyczne" formularze wywołania funkcji przekazują prawidłowe parametry funkcjom).

Centralnym punktem jest to, że oryginalny język (kernel) w ogóle nie miał wsparcia dla argumentów słów kluczowych i mogłem go dodać używając samego języka. Wynik jest dokładnie taki, jak gdyby był tam od początku; to po prostu część języka.

Składnia jest ważna (nawet jeśli technicznie możliwe jest tylko użycie maszyny Turinga). Składnia kształtuje twoje myśli. Makra (i odczyt makr) dają całkowitą kontrolę nad składnią.

Kluczową kwestią jest to, że przepisywanie kodu nie jest używaniem kalekiego języka dumbed down * * K-like jako metaprogramowania szablonów C++ (gdzie samo zrobienie if jest niesamowitym osiągnięciem), albo z jeszcze głupszym silnikiem o mniej niż regexp jak preprocesor C.

Kod-przepisywanie kodu wykorzystuje ten sam pełny (i rozszerzalny) język. It ' s lisp all the way down; -)

Oczywiście pisanie makr jest trudniejsze niż pisanie zwykłego kodu; ale jest to "zasadnicza złożoność" problemu, a nie sztuczna złożoność, ponieważ jesteś zmuszony używać głupiego języka pół - jak w przypadku metaprogramowania C++.

Pisanie makr jest trudniejsze, ponieważ kod jest rzeczą złożoną i pisząc makra piszesz złożone rzeczy, które same tworzą złożone rzeczy. To nawet nie jest rzadkością, aby przejść na wyższy poziom i pisać makra generujące makra (stąd pochodzi stary żart w Lispie "piszę kod, który pisze kod, który pisze kod, za który mi płacą").

Ale potęga makro jest po prostu nieograniczona.

 5
Author: 6502,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-29 14:10:48