【问题标题】:Using a macro for creating defrecord methods gives Don't know how to create ISeq from: clojure.lang.Symbol使用宏创建 defrecord 方法给出了不知道如何从以下位置创建 ISeq:clojure.lang.Symbol
【发布时间】:2015-03-11 13:42:23
【问题描述】:

在玩 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 中,它们都可以工作:(在这个缩小的示例中,方法不使用字段(xy))

(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))    
)    

没关系,但我想明白这一点。

【问题讨论】:

  • defrecord 本身就是一个宏。宏内部的宏可能很棘手。我会看看全球扩张。

标签: clojure macros


【解决方案1】:

defrecord 本身就是一个宏,因此不会对其子窗体执行宏扩展。 避免这种情况的一种快速方法是定义您自己的 my-defrecord,并使用您所需的方法实现扩展为 defrecord

【讨论】:

  • 啊,所以答案就在问题中:我忽略了一些琐碎的事情并且defrecord有交互。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多