2011-10-13 10 views
21

Sto cercando di creare un po 'di Clojure macro che def sa String con un tipo di suggerimento:Clojure defmacro perde metadati

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

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

Quando ho macroexpand esso, il tipo di suggerimento si perde:

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

Non importa la saggezza del tipo che suggerisce questo.

Perché la macro perde i metadati? Come scrivo questa macro o qualsiasi che includa i metadati?

risposta

32

^ è una macro di lettore. defmacro non arriva mai a vederlo. Il suggerimento è inserito nell'elenco (unquote name). Confronta per esempio (meta ^String 'x) con (meta ' ^String x) per vedere l'effetto.

È necessario inserire il suggerimento sul simbolo.

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

E l'utilizzo:

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! Ovviamente, le macro dei lettori vengono valutate prima di defmacros. Grazie. – Ralph

5

metadati non si presenta in una macroexpand in quanto si suppone di essere "invisibile".

Se la macro è corretta (che non lo è) dovresti essere in grado di chiamare (meta # 'db-host-option) per ispezionare i metadati sul var.

Si noti che (def sym ...) inserisce i metadati sulla var che riceve dal simbolo. Ma^Tag ~ nome imposta i metadati su ~ nome (nome non quotato), non sul simbolo passato passato al nome. Non può fare nient'altro dal momento che^Tag ... l'elaborazione viene eseguita dal lettore, che è già terminato all'avvio dell'espansione macro.

Volete qualcosa di simile

(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}