【发布时间】:2017-11-11 12:02:54
【问题描述】:
Clojure 中 set! 的惯用用例是什么?
找到用途并不容易!我搜索了一些更流行的 Clojure OSS 项目,但几乎没有。我能找到的唯一用途是设置像 warn-on-reflection 这样的全局变量,尽管目前还不清楚为什么这些不只是在本地使用 binding 设置。
有什么建议吗?
【问题讨论】:
标签: clojure
Clojure 中 set! 的惯用用例是什么?
找到用途并不容易!我搜索了一些更流行的 Clojure OSS 项目,但几乎没有。我能找到的唯一用途是设置像 warn-on-reflection 这样的全局变量,尽管目前还不清楚为什么这些不只是在本地使用 binding 设置。
有什么建议吗?
【问题讨论】:
标签: clojure
您已经回答了自己的问题。输入时:
(def foo 42)
(def ^:dynamic bar 43)
您正在创建一个全局变量。在大多数编程语言中,我们尽量减少使用全局变量,因为它们会引入更多复杂性和更难理解代码(这会导致更多错误!)。当我们确实需要全局变量时,我们会尽量减少这些全局变量的变异。这就是为什么在 Clojure 代码中很少看到 set!、var-set、var-get 和 alter-var-root 的原因。
当 Clojure 代码确实需要改变全局状态时,binding 通常是首选,因为当当前线程离开 binding 表单的范围时,所讨论的 var 会返回到其先前的状态。另一个好处是其他线程不受当前线程所做的任何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)
)
【讨论】:
set! 可以与线程本地binding 结合使用,以允许函数跨调用堆栈进行通信。这通常是一种在相互递归函数之间共享状态的便捷方式。
Clojure 编译器是一个很好的例子——尽管它是用 Java 实现的——使用 dynamic Vars 来跟踪内部状态。
【讨论】: