我正在尝试使用 Stuart Sierra 的 do-template
defprotocol
中的宏,并且 Clojure 编译器提示我正在重新定义 do-template
——不是我想要的:
(defprotocol AProtocol
(a-method [_])
(do-template [name]
`(~(symbol (str name "-method")) [this that])
foo
bar
baz))
这应该扩展到:
(defprotocol AProtocol
(a-method [_])
(foo-method [this that])
(bar-method [this that])
(baz-method [this that]))
问题(我相信)是 do-template
s-表达式被传递给 defprotocol
未展开。有没有办法让它先评估再通过?
顺便说一句,do-template
实际上应该扩展为
(do
(foo-method [this that])
(bar-method [this that])
(baz-method [this that]))
但我已经尝试过(使用手动扩展版本),并且 defprotocol
适合嵌套的 do
。
如何查看do-template
的实际扩展?我尝试了 (macroexpand '(do-template ...))
和 (macroexpand-1 '(do-template ...))
并得到:
(do (clojure.core/seq (clojure.core/concat (clojure.core/list (symbol (str foo "-method"))) (clojure.core/list (clojure.core/apply clojure.core/vector (clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/this)) (clojure.core/list (quote user/that)))))))) (clojure.core/seq (clojure.core/concat (clojure.core/list (symbol (str bar "-method"))) (clojure.core/list (clojure.core/apply clojure.core/vector (clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/this)) (clojure.core/list (quote user/that)))))))) (clojure.core/seq (clojure.core/concat (clojure.core/list (symbol (str baz "-method"))) (clojure.core/list (clojure.core/apply clojure.core/vector (clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/this)) (clojure.core/list (quote user/that)))))))))
不太容易阅读:-)。
此外,我可能希望 this
和 that
成为 anaphora 并扩展为自身:~'this
.
最佳答案
(1) defprotocol 不适用于 do 形式。它不会引发错误,但也不起作用。
(2) 你不能用这种方式做你想做的事。 defprotocol是被调用的宏,因此它对子表单如何展开有绝对的权力。
(3) 第 (2) 项提出了一种解决方案,实际上与您最近的至少一个问题相同:定义一个新宏,例如 with-methods
,它采用以下列表:方法名称,后跟任何其他 defprotocol 参数,并通过已完成的适当替换和拼接扩展为 defprotocol,以便 defprotocol 可以和平地扩展,而无需了解有关 do-template 技巧的任何信息。
关于macros - 强制扩展 Clojure 宏内的表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6402316/