What are the idiomatic use-cases for set!
in Clojure?
It's not easy to find uses! I grepped in some of the more popular Clojure OSS projects and there's almost none. The only uses I could find are for setting globals like warn-on-reflection, though it's unclear why these are not just set locally with binding
.
Any suggestions?
答案 0 :(得分:0)
您已回答了自己的问题。当您输入:
(def foo 42)
(def ^:dynamic bar 43)
您正在创建一个全局变量。在大多数编程语言中,我们尝试最小化使用全局变量,因为它们会引入更多复杂性并且更难以理解代码(这会导致更多错误!)。当我们确实需要全局变量时,我们会尽量减少这些全局变量的变异。这就是您很少看到Clojure代码中使用的set!
,var-set
,var-get
和alter-var-root
的原因。
当Clojure代码确实需要改变全局状态时,binding
通常是首选,因为当前线程离开var
形式的范围时,有问题的binding
返回到先前的状态。另一个好处是其他线程不受当前线程发生的任何binding
突变的影响。
有几种方法可以在Clojure中操作全局变量。与动态 var
相比,普通 var
的答案会有所改变。以下是一些例子:
(ns tst.clj.core
(:use clj.core
tupelo.test)
(:require
[tupelo.core :as t] ))
(t/refer-tupelo)
(def fred 0)
(def ^:dynamic *barney* 0) ; normally use "earmuffs" for dynamic vars
(def ^:dynamic wilma 0) ; you aren't forced to use use them (but you should!)
(def betty 29)
(dotest
(throws?
(binding [fred 1] ; can't bind non-dynamic var
(is= fred 1)
(throws? (set! fred 2))
(is= fred 2)))
(is= fred 0)
(throws? (set! fred 2)) ; can't set it
(is= fred 0)
(throws? (var-set #'fred 3)) ; java.lang.IllegalStateException: Can't change/establish root binding of: fred with set
(is= fred 0)
(is= (var-get #'fred) 0)
(binding [*barney* 3]
(is= *barney* 3)
(set! *barney* 4) ; works fine
(is= *barney* 4))
(is= *barney* 3) ; original value once leave binding scope
(binding [wilma 6]
(is= wilma 6)
(set! wilma 7) ; this works
(is= wilma 7)
(alter-var-root #'wilma inc) ; this doesn't work!
(is= wilma 7))
(is= wilma 1) ; the `inc` altered the root value, not the dynamic value in the `binding`
(alter-var-root #'wilma #(+ 23 %)) ; can alter root value outside of binding
(is= wilma 24)
(is= betty 29)
(alter-var-root #'betty inc) ; non-dynamic vars can have root value altered
(is= betty 30)
)
答案 1 :(得分:0)
set!
可以与线程本地binding
结合使用,以允许函数跨调用堆栈进行通信。它通常是跨相互递归函数共享状态的便捷方式。
Clojure编译器是一个很好的例子 - 即使它是用Java实现的 - 使用dynamic Vars来跟踪内部状态。