我有一些代码经过重构,只是发现某些内容被loop
破坏了。经过一些调试后,我发现loop
和with-redefs
不能很好地配合使用。我意识到在循环中使用with-redefs
可能没有意义,但我没想到它不会起作用。我不确定它是否是故意的。
这是我为演示“问题”而创建的MCVE:
(loop [test 3]
(with-redefs []
(if (zero? test)
"done"
(recur (dec test)))))
这给了我
重复出现的参数计数不匹配,预期:0 args,得到:1
删除with-redefs
的操作符合预期:
(loop [test 3]
(if (zero? test)
"done"
(recur (dec test))))
并返回"done"
。
第一段代码不起作用的原因是什么?这是故意的吗?
答案 0 :(得分:8)
解释在with-redefs
的宏扩展中:
(macroexpand-1
'(with-redefs []
(if (zero? test)
"done"
(recur (dec test)))))
返回:
(with-redefs-fn {}
(fn []
(if (zero? test)
"done"
(recur (dec test)))))
在其中您可以看到,因为引入了新的fn
,所以recur
将引用该fn
而不是更远的loop
(解释了Arity异常)。
还有许多其他与loop
“不兼容”的宏,因为recur
必须相对于loop
处于尾部位置,如果recur
发生在宏调用内,则该宏可能正在操纵代码,使得recur
不再位于尾部位置。
特别是对于with-redefs
(以及其他各种情况),一种解决方法可能是:
(loop [test 3]
(let [[recur? val]
(with-redefs []
(if (zero? test)
[false "done"]
[true (dec test)]))]
(if recur?
(recur val)
val)))