Clojure defmacro丢失了元数据

时间:2011-10-13 12:53:16

标签: macros clojure type-hinting

我正在尝试创建一个小的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")

不要介意暗示这一点的智慧。

为什么宏会丢失元数据?如何编写此宏或任何包含元数据的宏?

2 个答案:

答案 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}