我想编写一个包含大量表单的Clojure with-test-tags
宏,并在每个deftest
表单的名称中添加一些元数据 - 具体来说,将一些内容添加到{{1}我可以使用工具来运行带有特定标记的测试。
:tags
的一个明显实现是递归遍历整个身体,在我找到它时修改每个with-test-tags
表单。但我最近一直在阅读 Let Over Lambda ,他提出了一个很好的观点:不要自己走代码,只需将代码包装在deftest
中,然后让编译器将其转换为您。类似的东西:
macrolet
这有一个明显的问题,即(defmacro with-test-tags [tags & body]
`(macrolet [(~'deftest [name# & more#]
`(~'~'deftest ~(vary-meta name# update-in [:tags] (fnil into []) ~tags)
~@more#))]
(do ~@body)))
(with-test-tags [:a :b]
(deftest x (...do tests...)))
宏继续以递归方式永久扩展。我可以将其扩展为deftest
,从而避免任何进一步的递归扩展,但是我无法将clojure.test/deftest
的实例嵌套到标签测试的子组中。
此时,特别是对于像with-test-tags
这样简单的事情,看起来自己走代码会更简单。但是我想知道是否有人知道编写宏的技术,它会“略微修改”某些子表达式,而不会永远递归。
对于好奇:我考虑过其他一些方法,比如我有一个编译时deftest
- 我设置为上下代码的var,并在我最终看到{时使用该var {1}},但由于每个宏只返回一个扩展,因此下一次调用macroexpand时,它的绑定将不会到位。
我刚才做了一个postwalk实现,虽然它有效但它不尊重binding
这样的特殊形式 - 它也会扩展到那些内部。
deftest
(另外,对于common-lisp标签上可能存在的噪音感到抱歉 - 我认为即使只有最小的Clojure经验,你也可以帮助我们处理weirder宏的东西。)
答案 0 :(得分:5)
答案 1 :(得分:0)
至少使用Common Lisp,您可以简单地给阴影宏起别名。像这样:
(setf (macro-function 'deftest2) (macro-function 'deftest))
(defmacro with-test-tags (etc...)
`(macrolet ((deftest (etc...)
``(deftest2 ...
Clojure应该有类似的东西。此处讨论了该主题:define a synonym for a Clojure macro。请注意,定义扩展为“ deftest”调用的“ deftest2”宏可能不起作用。
我看到这个答案有点晚了,但是我会在这里张贴给路人。