使用Clojure,我尝试使用宏来为defrecord
中的协议创建方法,但是我遇到了我无法理解的类型错误。
使用这个最小协议
(defprotocol fooprot
(ffoo [_ v])
)
我想用宏来定义方法。以下是两次使用不同引号的非工作尝试:
(defmacro mk-m1
[fnname value]
`(~fnname [~'_ ~'p]
(str ~'p ~value )))
(defmacro mk-m2
[fnname value]
(list fnname '[_ p]
(list 'str 'p value )))
扩展它们会提供相同的输出(命名空间和列表类型除外)
(macroexpand '(mk-m1 ffoo "foo")) ; => (ffoo [_ p] (clojure.core/str p "foo"))
(macroexpand '(mk-m2 ffoo "foo")) ; => (ffoo [_ p] (str p "foo"))
如果我将该输出复制并粘贴到defrecord
,它们都可以正常工作:(在这个按比例缩小的示例中,这些方法不使用字段(x
,y
))
(defrecord myrec1 [x y]
fooprot
(ffoo [_ v] (str v "foo"))
)
(defrecord myrec2 [x y]
fooprot
(ffoo [_ p] (clojure.core/str p "foo"))
)
(ffoo (->myrec1 1 2) "hello ") ; => "hello foo"
(ffoo (->myrec2 1 2) "hello ") ; => "hello foo"
现在,当我尝试使用宏来定义方法时,我得到IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol
:
(defrecord myrec-m1 [x y]
fooprot
(mk-m1 ffoo "foo")
)
; => IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol clojure.lang.RT.seqFrom (RT.java:505)
(defrecord myrec-m2 [x y]
fooprot
(mk-m2 ffoo "foo")
)
; => IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol clojure.lang.RT.seqFrom (RT.java:505)
......这就是我迷失的地方。哪个Symbol
是问题?看着
宏扩展到什么,对我来说似乎是合理的:
(let [ex (macroexpand '(mk-m1 ffoo "foo"))]
(println ex " : " (type ex))
(doseq [tmp ex] (println tmp " : " (type tmp))))
; => (ffoo [_ p] (clojure.core/str p foo)) : clojure.lang.Cons
; ffoo : clojure.lang.Symbol
; [_ p] : clojure.lang.PersistentVector
; (clojure.core/str p foo) : clojure.lang.Cons
; nil
和
(let [ex (macroexpand '(mk-m2 ffoo "foo"))]
(println ex " : " (type ex))
(doseq [tmp ex]
(println tmp " : " (type tmp))) )
; => (ffoo [_ p] (str p foo)) : clojure.lang.PersistentList
; ffoo : clojure.lang.Symbol
; [_ p] : clojure.lang.PersistentVector
; (str p foo) : clojure.lang.PersistentList
; nil
其中向量元素也是符号:
(doseq [e (nth (macroexpand '(mk-m1 ffoo "foo"))1)]
(println e " : " (type e)))
;=> _ : clojure.lang.Symbol
; p : clojure.lang.Symbol
; nil
我错过了什么?我是否忽略了一些微不足道的事情,或者是否与defrecord
宏进行了一些互动?
一个可行的解决方法是让宏创建函数,然后在方法中调用那些生成的函数
(defrecord myrec1 [x y]
fooprot
(ffoo [this v] (generated-ffoo this v))
)
这没关系,但我想理解这一点。
答案 0 :(得分:3)
defrecord
本身就是一个宏,因此不会对其子窗体执行宏扩展。
避免这种情况的一种快速方法是定义您自己的my-defrecord
,使用您想要的方法推广扩展为defrecord
。