当我在REPL上输入时:
> (defprotocol protocolname (methodname [a b]))
这很有效。
protocolname
同样当我在REPL上输入它时:
> (defprotocol protocolname (methodname ['a 'b]))
有效。
protocolname
但是当我在REPL上输入它时:
> (let [arg-vec ['a 'b]]
(defprotocol protocolname (methodname arg-vec)))
失败并出现此错误:
CompilerException java.lang.IllegalArgumentException: Parameter declaration missing,
我的问题是: 为什么这个Clojure协议声明会因 java.lang.IllegalArgumentException: Parameter declaration missing
意外失败?
更新 - 感谢@Mars - 我们还提供了一些其他信息:
线的宏展开
(pprint (macroexpand-1 '(defprotocol protocolname (methodname [a b]))))
包含以下行:
:sigs
'{:methodname {:doc nil, :arglists ([a b]), :name methodname}}
该行的宏观扩展:
(let [arg-vec ['a 'b]]
(pprint (macroexpand-1
'(defprotocol protocolname (methodname arg-vec)))))
有这条线:
'{:methodname {:doc arg-vec, :arglists nil, :name methodname}}
这告诉我们,当您将vector var传递给defprotocol
声明时 - var将分配给:doc
而不是:arglist
。 (但是当您在dedprotocol
调用中声明向量时,它会将向量分配给:arglist
。
所以我要更新这个问题。
现在的问题是:如何将参数列表作为Clojure中协议声明的向量传递?
答案 0 :(得分:1)
其他人将能够给出更深层次的答案,但以下的永无止境似乎值得提供答案:
如果你执行
(pprint (macroexpand-1 '(defprotocol protocolname (methodname ['a 'b]))))
和
(pprint (macroexpand-1 '(defprotocol protocolname (methodname [a b]))))
然后将它们的输出与
的输出进行比较(let [arg-vec ['a 'b]]
(pprint (macroexpand-1
'(defprotocol protocolname (methodname arg-vec)))))
你会发现宏defprotocol
的扩展方式存在显着差异。 (我不会在这里重现扩展,因为前两个很长。)
宏展开过程会查看实际的预评估参数。快速浏览一下扩展显示,当methodname
之后的表单是一个序列时,每个序列的元素都是单独处理的,而当表单是arg-vec
时,它只是arg-vec
,没有评估,由扩展过程处理。
答案 1 :(得分:0)
有工作解决方案,但它并不好(因为eval
使用):
(defmacro defprotocol-with-args [name methodname args-coll]
`(defprotocol ~name (~methodname [~@(eval args-coll)])))
您可以将[~@(eval args-coll)]
更改为~(eval args-coll)
,但之后它只能用于向量(上面的代码适用于任何平符号序列)。
试验:
(defprotocol-with-args protocolname methodname '[a b]) ; => protocolname
(def args '[a b]) ; => #'user/args
(defprotocol-with-args protocolname methodname args) ; => protocolname
(macroexpand-1 `(defprotocol-with-args protocolname methodname args)) ; => (clojure.core/defprotocol user/protocolname (user/methodname [a b]))
(macroexpand-1 `(defprotocol-with-args protocolname methodname '[a b])) ; => (clojure.core/defprotocol user/protocolname (user/methodname [user/a user/b]))
注意仅在REPL中测试。在一般情况下,很难预测这种方法的表现如何。