Clojure的defmacro丢失元数据。

25

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

37

^ 是一个阅读器宏。 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
啊!当然,阅读器宏在 defmacro 之前被求值。谢谢。 - Ralph

6

元数据在宏展开中不会显示出来,因为它应该是“不可见”的。

如果宏是正确的(实际上并不是),您应该能够调用(meta#'db-host-option)来检查变量上的元数据。

请注意,(def sym ...)会在接收到符号的变量上插入元数据。但是^Tag ~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}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接