Clojure: binding和with-redefs的区别

32

clojure.core有宏bindingswith-redefs。通过查看clojuredocs.org上的文档字符串和示例,它们似乎做了非常相似的事情。它们之间有什么区别,我应该在哪些情况下使用哪个?


3
请参考这个问题:https://dev59.com/UGUo5IYBdhLWcg3wzSIq#15748334 - Alex
1个回答

36

Clojure变量可以有线程局部绑定。 binding 使用这些绑定,而 with-redefs 实际上会更改变量的根绑定(类似于默认值)。

另一个区别是 binding 仅适用于 :dynamic 变量,而 with-redefs 适用于所有变量。

示例:

user=> (def ^:dynamic *a* 1)
#'user/*a*
user=> (binding [*a* 2] *a*)
2
user=> (with-redefs [*a* 2] *a*)
2
user=> (binding [*a* 2] (doto (Thread. (fn [] (println "*a* is " *a*))) (.start) (.join)))
*a* is  1
#<Thread Thread[Thread-2,5,]>
user=> (with-redefs [*a* 2] (doto (Thread. (fn [] (println "*a* is " *a*))) (.start) (.join)))
*a* is  2
#<Thread Thread[Thread-3,5,]>

您可以使用(未记录在案的)binding-conveyor-fn来将线程本地绑定传递到新线程中:

user=> (binding [*a* 2] (doto (Thread. (#'clojure.core/binding-conveyor-fn (fn [] (println "*a* is " *a*)))) (.start) (.join)))
*a* is  2
#<Thread Thread[Thread-5,5,]>

6
这就是为什么 with-redefs 适用于测试(你可能想要调用并替换一个函数),而 binding 在生产代码中也很有用。 - Peeja
1
@Peeja 谢谢,换句话说,在多线程环境中永远不应该使用 with-redefs 吗? - Erik Kaplun
截至2019年6月,Vars文档中有一个“绑定传送”章节(https://clojure.org/reference/vars#conveyance),其中强调了 futuresendsend-offpmap 具有绑定传输的能力 - 这是否意味着不再需要 binding-conveyor-fn - Erik Kaplun
1
@ErikKaplun,如果您使用了您提到的函数,则不需要binding-conveyor-fn。但是,如果您使用(Thread. ...)创建线程,则仍然需要它。 - Miikka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接