Clojure重复`defmacro`包装一个创建Java类的表单

时间:2018-03-22 02:23:04

标签: clojure macros

我正在用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的核心贡献者必须遇到这个问题吗?这是怎么处理的?为什么我没有在defrecordemit-defrecord的Clojure核心源中看到明确的解决方案?我错过了什么?

1 个答案:

答案 0 :(得分:1)

嗯,我是个白痴。我在"欢乐的Clojure"中找到了这个:

(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例外。

这似乎与我不一致,但我知道什么?