2012-03-13 16 views
10

Quiero escribir una macro que utiliza funciones de la biblioteca clj-time. En un espacio de nombres me gustaría llamar a la macro como esta:Espacio de nombres confusión y macros

(ns budget.account 
    (:require [budget.time])) 

(budget.time/next-date interval frequency) 

La macro siguiente día se define en otro archivo de la siguiente manera:

(ns budget.time 
    (:require [clj-time.core :as date])) 

(defmacro next-date [interval freq] 
    `(~interval ~freq)) 

Si la macro se llama con los siguientes argumentos (budget.time/next-date interval freq) e interval y freq donde "weeks" y "2" respectivamente la macro expand se vería así (clj-time.core/weeks 2)

Cada vez que intento esto desde el REPL no puede resolver el espacio de nombres.

¿Hay alguna manera de forzar a la macro a resolver el intervalo entre los argumentos del espacio de nombres clj-time? ¿Cuál es la mejor manera de hacer esto?

Gracias!

+0

¿Por qué necesita escribir una macro. ¿No podría hacerse esto con una función regular? Recuerde: nunca debe escribir una macro cuando una función lo hará. – viebel

Respuesta

10

Las macros devuelven una lista que luego se evalúa en el espacio de nombres desde el que se llama, no el espacio de nombre en el que está definido. Esto es diferente de las funciones que se evalúan en el espacio de nombres en el que se definieron. Esto es porque las macros devuelven el código que se ejecutará, en lugar de solo ejecutarlo.

si voy a otro espacio de nombres, por ejemplo hello.core y ampliar una llamada al siguiente día me sale:

hello.core> (macroexpand-1 '(next-date weeks 2)) 
(weeks 2) 

continuación, después de la expansión, se resuelve semanas de hello.core, en la cual es por supuesto no definido. para solucionar esto, necesitamos el símbolo devuelto para llevar la información de espacio de nombres con él.

Afortunadamente puede resolver explícitamente un símbolo en un espacio de nombres con ns-resolve. Se necesita un espacio de nombres y un símbolo y trata de encontrarlo en la nula volviendo espacio de nombres si no se encuentra

(ns-resolve 'clj-time.core (symbol "weeks")) 
#'clj-time.core/weeks 

próxima su macro va a tomar un símbolo y un número por lo que se puede prescindir de la llamada explícita a symbol

(ns-resolve 'clj-time.core 'weeks) 
#'clj-time.core/weeks 

por lo que ahora sólo tiene una función que resuelve la función y luego crea una lista de la función se ha resuelto, seguido del número,

(defmacro next-date [interval freq] 
    (list (ns-resolve 'clj-time.core interval) freq)) 

En todo lo que hace por encima de macro es hacer una llamada a la función que se llama inmediatamente, por lo que ni siquiera necesita una macro para esto:

(defn next-date [interval freq] 
    ((ns-resolve 'clj-time.core interval) freq)) 
(next-date 'weeks 2) 
#<Weeks P2W> 

la versión no requiere macro que citar el intervalo porque es necesario que no se evalúe antes de poder buscarlo. Lo que la macro realmente te compra aquí no es tener que incluir la cotización, a costa de requerir que todas las personas que llaman requieran clj-time

, por supuesto, también podrías necesitar clj-time en todas partes, pero ese no es realmente el punto .

+1

¿Es mejor requerir clj-time en todas partes o usar ns-resolve? – user1265564

+1

personalmente veo que se requiere tiempo de clj en todas partes como la opción más limpia. –

Cuestiones relacionadas