2011-10-13 11 views
21

Estoy intentando crear una macro poco Clojure que def Cadena SA con un toque tipo:Clojure defmacro pierde metadatos

(defmacro def-string [name value] 
    `(def ^String ~name ~value)) 

(def-string db-host-option "db-host") 

Cuando macroexpand ella, se pierde la pista Tipo:

(macroexpand '(def-string db-host-option "db-host")) 
;=> (def db-host-option "db-host") 

No importa la sabiduría del tipo que insinúa esto.

¿Por qué la macro pierde los metadatos? ¿Cómo escribo esta macro, o cualquiera que incluya metadatos?

Respuesta

32

^ es una macro de lector. defmacro nunca llega a verlo. La pista se coloca en la lista (unquote name). Compare por ejemplo (meta ^String 'x) con (meta ' ^String x) para ver el efecto.

Debe poner la sugerencia en el símbolo.

(defmacro def-string 
    [name value] 
    `(def ~(vary-meta name assoc :tag `String) ~value)) 

Y el uso:

user=> (def-string foo "bar") 
#'user/foo 
user=> (meta #'foo) 
{:ns #<Namespace user>, :name foo, :file "NO_SOURCE_PATH", :line 5, :tag java.lang.String} 
+1

Ahh! Por supuesto, las macros de lector se evalúan antes de defmacros. Gracias. – Ralph

5

metadatos no se presenta en un macroexpand ya que se supone que es "invisible".

Si la macro es correcta (que no es) debería poder llamar (meta # 'db-host-option) para inspeccionar los metadatos en la var.

Tenga en cuenta que (def sym ...) inserta metadatos en la var que recibe del símbolo. Pero^Tag ~ name establece los metadatos en ~ nombre (nombre de comilla), no en el símbolo pasado asociado al nombre. No puede hacer nada más ya que el procesamiento^Tag ... lo realiza el lector, que ya está terminado una vez que se inicia la expansión de macro.

¿Quieres algo así como

(defmacro def-string [name value] 
    `(def ~(with-meta name {:tag String}) ~value)) 


user> (def-string bar 1) 
#'user/bar 
user> (meta #'bar) 
{:ns #<Namespace user>, :name bar, :file "NO_SOURCE_FILE", :line 1, :tag java.lang.String} 
Cuestiones relacionadas