Clojure defmacro丢失了元数据

Ral*_*lph 24 macros clojure type-hinting

我正在尝试创建一个def带有类型提示的字符串Clojure宏:

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

(def-string db-host-option "db-host")
Run Code Online (Sandbox Code Playgroud)

当我macroexpand这样,类型提示丢失:

(macroexpand '(def-string db-host-option "db-host"))
;=> (def db-host-option "db-host")
Run Code Online (Sandbox Code Playgroud)

别担心提示这种类型的智慧.

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

kot*_*rak 35

^读者宏.defmacro从来没有看到它.提示被列入清单(unquote name).比较例如(meta ^String 'x)(meta ' ^String x)看到效果.

你需要把提示放在符号上.

(defmacro def-string
  [name value]
  `(def ~(vary-meta name assoc :tag `String) ~value))
Run Code Online (Sandbox Code Playgroud)

用法:

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}
Run Code Online (Sandbox Code Playgroud)


Joo*_*aat 6

元数据不会出现在宏扩展中,因为它应该是"不可见的".

如果宏是正确的(它不是),你应该能够调用(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}
Run Code Online (Sandbox Code Playgroud)