2010-06-04 32 views
13

que define una macro unless de la siguiente manera:Definición Clojure sintaxis macro

user=> (defmacro unless [expr body] (list 'if expr nil body)) 
#'user/unless 
user=> (unless (= 1 2) (println "Yo")) 
Yo 

Como se puede ver que funciona bien.

Ahora, en Clojure una lista se puede definir de dos maneras:

; create a list 
(list 1 2 3) 

; shorter notation 
'(1 2 3) 

Esto significa que el unless macro se puede escribir sin la palabra clave list. Sin embargo, esto da como resultado una excepción de Java lanzada:

user=> (unless (= 1 2) (println "Yo")) 
java.lang.Exception: Unable to resolve symbol: expr in this context 

¿Alguien puede explicar por qué esto falla?

+6

FYI, Clojure ya tiene macros similares en el núcleo, llamados 'when-not' y' if-not'. –

Respuesta

15

'(foo bar baz) no es un atajo para (list foo bar baz), es un atajo para (quote (foo bar baz)). Mientras que la versión de la lista devolverá una lista que contiene los valores de las variables foo, bar y baz, la versión con ' devolverá una lista que contiene los símbolos foo, bar y baz. (En otras palabras '(if expr nil body) es lo mismo que (list 'if 'expr 'nil 'body).

Esto conduce a un error, ya que con la versión citado la macro se expande a (if expr nil body) en lugar de (if (= 1 2) nil (println "Yo")) (porque en lugar de sustituir los argumentos de la macro de expr y el cuerpo, simplemente devuelve el nombre expr y el cuerpo (que se ve entonces como variables no existentes en el código expandido).

Un acceso directo que es útil en definiciones de macro está utilizando `. ` obras como ' (es decir, que cita a la expresión siguiente), pero le permite evaluar algunas subexpresiones sin comillas usando ~. Por ejemplo, su macro podría reescribirse como (defmacro unless [expr body] `(if ~expr nil ~body)). Lo importante aquí es que expr y body no se citan con ~. De esta forma, la expansión contendrá sus valores en lugar de contener literalmente los nombres expr y body.

+0

Además '\' 'se ocupará de que la macro sea higiénica. 'list' nunca debería usarse para definir macro expansiones. – kotarak

+0

Y un breve comentario para la macro en sí: puede soportar el 'do' implícito para permitir múltiples formularios en el cuerpo de la macro. '(defmacro a menos que [expr & body] \' (si ~ expr nil (do ~ @ body))) ' – kotarak

+1

el uso de la lista es bueno si se escapa la mayoría de sus formularios; '(defmacro my-if [pred then else] \' (si ~ pred ~ luego ~ else)) 'vs' (defmacro my-if [pred then else] (lista \ 'si pred entonces else))', por ejemplo . –

Cuestiones relacionadas