我正在尝试创建一个小的Clojure宏,def
是一个带有类型提示的字符串:
(defmacro def-string [name value]
`(def ^String ~name ~value))
(def-string db-host-option "db-host")
当我macroexpand
时,类型提示会丢失:
(macroexpand '(def-string db-host-option "db-host"))
;=> (def db-host-option "db-host")
不要介意暗示这一点的智慧。
为什么宏会丢失元数据?如何编写此宏或任何包含元数据的宏?
答案 0 :(得分:33)
^
是一个读者宏。 defmacro
永远不会看到它。提示放在列表(unquote name)
上。比较例如(meta ^String 'x)
和(meta ' ^String x)
以查看效果。
您需要将提示放在符号上。
(defmacro def-string
[name value]
`(def ~(vary-meta name assoc :tag `String) ~value))
用法:
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 :(得分:5)
元数据不会出现在宏扩展中,因为它应该是“不可见的”。
如果宏是正确的(它不是),你应该能够调用(meta#'db-host-option)来检查var上的元数据。
请注意(def sym ...)在从符号接收的var上插入元数据。但是^ Tag~name在~name(unquote name)上设置元数据,而不是在绑定到name的传入符号上。它不能做任何其他事情,因为^ Tag ...处理由读者完成,一旦宏扩展开始就已经完成。
你想要像
这样的东西(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}