我正在用defrecord
包裹defmacro
,因为我想要一个特定类型的具体地图来注册自己的中心位置(对于serde)。
宏是:
(defmacro def-thing! [name [& fields] & opts+specs]
`(let [klass# (defrecord ~name ~fields ~@opts+specs)
map-constructor-fn# (resolve (symbol (str 'map-> ~name)))]
; use the map-constructor-fn#
))
问题是let
形式的宏扩展方式不同,这取决于之前是否已调用过宏(就像我在nrepl中重新加载命名空间源文件一样)。
第一次,name
是符号。
第二次,name
是一个Java类。
当然,我可以测试name~
是否是Java类,然后调用.getSimpleName()
并执行相应的操作。
但是Clojure的核心贡献者必须遇到这个问题吗?这是怎么处理的?为什么我没有在defrecord
或emit-defrecord
的Clojure核心源中看到明确的解决方案?我错过了什么?
答案 0 :(得分:1)
(defmacro def-thing! [name [& fields] & opts+specs]
`(let [klass# (defrecord ~name ~fields ~@opts+specs)
map-constructor-fn# (resolve (symbol (str 'map-> '~name)))]
; use the map-constructor-fn#
))
请注意(quote (unquote <symbol>))
- 即'~<symbol>
- 表单。这个成语强制捕获传入的原始,不合格的符号。
奇怪的是,我 不 将其传递给defrecord
来电。在内部,defrecord
执行相同的原始符号捕获,除非通过var
调用,大概是因为defrecord
,当第一次调用时,会将符号分配给完全限定的类var。事实上,'~name
具体 无效 与defrecord
调用,我得到clojure.lang.Cons cannot be cast to clojure.lang.Named
例外。
这似乎与我不一致,但我知道什么?