Tworzenie list jawnie jest "najprostsze", w pewnym sensie, ponieważ istnieje kilka podstawowych pojęć, które musisz znać: po prostu zaakceptuj listę i zmień ją, dopóki nie uzyskasz nowej listy. Backtick jest wygodnym skrótem do "szablonowania" fragmentów kodu; możliwe jest zapisanie dowolnego makra bez niego, ale dla każdego dużego makra szybko staje się bardzo nieprzyjemne. Na przykład, należy rozważyć dwa sposoby pisania let
jako makro ponad fn
:
(defmacro let [bindings & body]
(let [names (take-nth 2 bindings)
vals (take-nth 2 (rest bindings))]
`((fn [[email protected]]
(do [email protected]))
[email protected])))
(defmacro let [bindings & body]
(let [names (take-nth 2 bindings)
vals (take-nth 2 (rest bindings))]
(cons (list `fn (vec names) (cons `do body))
vals)))
W pierwszym przypadku, przy użyciu znaku odwróconego apostrofu czyni to dość oczywiste, że piszesz funkcję nazw zawierających ciało, a następnie wywołanie to z wartościami - kod makro jest "ukształtowany" tak samo jak kod ekspansji, więc możesz sobie wyobrazić, jak będzie wyglądać.
W drugim przypadku, z tylko cons
i list
w każdym miejscu, to prawdziwy ból głowy, aby dowiedzieć się, jak będzie wyglądać rozszerzenie. Oczywiście nie zawsze tak jest: czasami łatwiej jest napisać coś bez backtick.
Innym bardzo ważnym punktem została wykonana przez Kyle Burton: print
nie jest taka sama jak 'print
! Twoja ekspansja makra powinna zawierać symbol symbolprint
, a nie jego wartość (która jest funkcją). Osadzanie obiektów (takich jak funkcje) w kodzie jest bardzo delikatne i działa tylko przez przypadek. Upewnij się więc, że makra rozwijają się do kodu, który mógłbyś sam napisać, i niech system ewaluacyjny wykona ciężką pracę - możesz wpisać symbol print
, ale nie możesz wpisać wskaźnika do bieżącej wartości funkcji print
.
Jest to właściwie odpowiednik '(list \' print a) ', który jest nieco inny:' 'print' będzie działał tylko wtedy, gdy wywołujący ma 'print' odwołujący się do' clojure.core/print' zamiast lokalnego, lub nie ma żadnego wiązania (powiedzmy, że wykluczyli je z ': refer-clojure' ich przestrzeni nazw). Z kolei '' 'print' rozwija się bezpośrednio na' clojure.core/print', więc jest niewiarygodny i poprawny w każdym kontekście. – amalloy
@amalloy Dzięki za odpowiedź, na podstawie Twojej sugestii po prostu próbowałem zrobić makroexpand (przez szlam) na '' '(print ~ a)' i wróciłem: '(seq (concat (list 'print) (lista a))) '- ah, ale mówisz, że pre-transformowana forma jest bardziej podobna do' '' print', czyż nie? –
Nie mogę odczytać twojej odpowiedzi bardzo dobrze, ponieważ SO je niektóre postacie, ale: Slime ukrywa przestrzeń nazw od ciebie (i makroexpand nie jest konieczne). Jeśli tylko zacytujesz wyrażenie, wpisując '' \ '(print ~ a) 'w repl, zobaczysz, że jest to odpowiednik' (clojure.core/seq (clojure.core/concat (clojure.core/list (quote clojure.core/print)) (clojure.core/list a))) '. 'Clojure.core/print' to ważne rozróżnienie, które robiłem. – amalloy