Construir listas explícitamente es "más simple", de alguna manera, porque hay pocos conceptos básicos que necesita saber: simplemente acepte una lista y cámbiela hasta que tenga una nueva lista. Backtick es un atajo conveniente para "moldear" trozos de código; es posible escribir cualquier macro sin él, pero para cualquier macro grande se vuelve muy desagradable rápidamente. Por ejemplo, considere dos formas de escribir let
como una macro sobre 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)))
En el primer caso, el uso de comillas invertidas hace que sea bastante claro que usted está escribiendo una función de los nombres que contienen el cuerpo y, a continuación, llamar con los valores: el código de macro tiene "forma" igual que el código de expansión, por lo que puede imaginarse cómo se verá.
En el segundo caso, con solo cons
y list
en todas partes, es un verdadero dolor de cabeza descubrir cómo se verá la expansión. Este no es siempre el caso, por supuesto: a veces puede ser más claro escribir algo sin un retroceso.
Otro punto muy importante fue hecho por Kyle Burton: print
no es lo mismo que 'print
! La expansión de macro debe contener el símboloprint
, no su valor (que es una función). Incrustar objetos (como funciones) en el código es muy frágil y solo funciona por accidente. Así que asegúrese de que sus macros se amplíen al código que realmente podría haber escrito usted mismo, y deje que el sistema de evaluación haga el trabajo duro; podría escribir el símbolo print
, pero no podría escribir un puntero al valor actual de la función print
.
En realidad es equivalente a '(list \' print a) ', que es ligeramente diferente:' 'print' solo funcionará si la persona que llama tiene 'print' refiriéndose a' clojure.core/print' en lugar de a un local, o no vinculante en absoluto (digamos que lo excluyeron del ': refer-clojure' de su espacio de nombres). '\' print', por otro lado, se expande directamente a 'clojure.core/print', por lo que no es ambiguo y es correcto en cualquier contexto. – amalloy
@amalloy Gracias por responder, basándome en su sugestión, traté de hacer un macroexpand (mediante baba) en '' '(print ~ a)' y volví: '(seq (concat (list 'print) (list a))) '- ah, pero estás diciendo que la forma pre-transformada es más similar a tener' '' print '¿no? –
No puedo leer su respuesta muy bien porque SO está comiendo algunos de los caracteres retrospectivos, pero: Slime está ocultando los espacios de nombres (y macroexpand no es necesario). Si solo cita la expresión, al escribir ''\' (print ~ a) 'en la réplica, verá que es equivalente a' (clojure.core/seq (clojure.core/concat (clojure.core/list (quote clojure.core/print)) (clojure.core/list a))) '. El 'clojure.core/print' es la distinción importante que estaba haciendo. – amalloy