Costruire liste in modo esplicito è "più semplice", in un certo senso, perché ci sono alcuni concetti chiave che devi sapere: basta accettare una lista e cambiarla finché non si ha una nuova lista. Backtick è una comoda scorciatoia per "templare" blocchi di codice; è possibile scrivere qualsiasi macro senza di essa, ma per qualsiasi macro grande diventa rapidamente molto spiacevole. Per esempio, consideriamo due modi di scrivere let
come una macro su 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)))
Nel primo caso, utilizzando backtick rende abbastanza chiaro che si sta scrivendo una funzione dei nomi che contengono il corpo, e quindi chiamando con i valori - il codice macro è "modellato" come il codice di espansione, quindi puoi immaginare come sarà.
Nel secondo caso, con solo cons
e list
dappertutto, è un vero problema trovare l'aspetto che avrà l'espansione. Naturalmente non è sempre così: a volte può essere più chiaro scrivere qualcosa senza un apice.
Un altro punto molto importante è stato fatto da Kyle Burton: print
non è lo stesso di 'print
! L'espansione della macro deve contenere il simbolo print
, non il suo valore (che è una funzione). Incorporare oggetti (come le funzioni) nel codice è molto fragile e funziona solo per sbaglio. Quindi assicurati che le macro si espandano al codice che potresti effettivamente aver scritto da te e che il sistema di valutazione faccia il duro lavoro - potresti digitare il simbolo print
, ma non puoi digitare un puntatore al valore corrente della funzione print
.
In realtà è equivalente a '(list \' print a) ', che è leggermente diverso:' 'print' funziona solo se il chiamante ha 'print' che si riferisce a' clojure.core/print' piuttosto che a un locale, o nessun legame (diciamo che l'hanno escluso dal ': refer-clojure' del loro spazio dei nomi). '\' print', d'altra parte, si espande direttamente a 'clojure.core/print', quindi non è necessario e corretto in qualsiasi contesto. – amalloy
@amalloy Grazie per aver risposto, in base alla tua congestione ho appena provato a fare un macroexpand (via slime) su '' '(print ~ a)' e sono tornato: '(seq (concat (lista 'print) (lista a))) '- ah, ma stai dicendo che la forma pre-trasformata è più simile ad avere' '' print' 'vero? –
Non riesco a leggere la tua risposta molto bene perché SO sta mangiando alcuni dei caratteri retrospettivi, ma: Slime nasconde i namespace da te (e macroexpand non è necessario). Se si cita l'espressione, digitando ''\' (print ~ a) 'nel repl, si vede che è equivalente a' (clojure.core/seq (clojure.core/concat (clojure.core/list (quote clojure.core/print)) (clojure.core/list a))) '. Il 'clojure.core/print' è l'importante distinzione che stavo facendo. – amalloy