`loop`和`with-redefs`不能很好地融合在一起

Reu*_*ani 2 clojure

我有一些代码,我重构只是为了找出被破坏的东西loop.经过一些调试后,我发现loop并且with-redefs不能很好地一起玩.我意识到with-redefs在循环中使用它可能没有意义,但我没想到它不起作用.我不确定它是否有意.

这是我创建的MCVE,用于演示"问题":

(loop [test 3]
  (with-redefs []
    (if (zero? test)
      "done"
      (recur (dec test)))))
Run Code Online (Sandbox Code Playgroud)

这给了我:

不匹配的参数计数重复,预期:0 args,得到:1

with-redefs按预期删除工作:

(loop [test 3]
  (if (zero? test)
    "done"
    (recur (dec test))))
Run Code Online (Sandbox Code Playgroud)

并返回"done".

第一段代码不起作用的原因是什么?这是故意的吗?

gfr*_*cks 8

解释是在宏观扩展中with-redefs:

(macroexpand-1
 '(with-redefs []
    (if (zero? test)
      "done"
      (recur (dec test)))))
Run Code Online (Sandbox Code Playgroud)

收益:

(with-redefs-fn {}
  (fn []
    (if (zero? test)
      "done"
      (recur (dec test)))))    
Run Code Online (Sandbox Code Playgroud)

在那里你可以看到,因为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)))
Run Code Online (Sandbox Code Playgroud)