不能 change/establish 的根绑定:[some-def] 与 Clojure 中的 set

Can't change/establish root binding of: [some-def] with set in Clojure

我无法将动态变量的值设置为新值。

(def *pop* true)

(set! *pop* false)

=> IllegalStateException Can't change/establish root binding of: *pop* with set  clojure.lang.Var.set (Var.java:221)


我还添加了 ^:dynamic,但也没有用。

(def ^:dynamic *pop* true)

(set! *pop* false)

=> IllegalStateException Can't change/establish root binding of: *pop* with set  clojure.lang.Var.set (Var.java:221)


但另一方面,这段代码有效,(clojure core's var -> *warn-on-reflection*)

(set! *warn-on-reflection* true)
=> true

*warn-on-reflection*
=> true

(set! *warn-on-reflection* false)
=> false

*warn-on-reflection*
=> false

动态变量只能 set!binding 范围内。因此,仅在 *pop* 上调用 set! 是行不通的 - 您需要在上面调用堆栈中某处绑定的运行时动态范围内。

(def ^:dynamic *pop* true)
(binding [*pop* *pop*]  ;; defaulted to the root binding value
  (set! *pop* false)    ;; ok, because in dynamic binding scope
  (println *pop*))      ;; prints "false" (inside per-thread dynamic binding)
(println *pop*)         ;; prints "true" (root value)

请注意,"dynamic scope" 部分意味着在 binding 中,您可以进行任意嵌套调用,并且仍然可以设置和读取 [=14= 的 per-thread 值].

关于 *warn-on-reflection*,这看起来像是特殊行为,但实际上完全一样,除了隐藏在视图之外。 REPL 本身围绕每个 REPL 语句的 eval 创建了一个动态绑定范围,并绑定了一组 hard-coded 动态变量,其中 *warn-on-reflection* 是一个。您可以找到那组绑定 here.

您可以使用 alter-var-root 更改根变量。

user=> (def *pop* true)
Warning: *pop* not declared dynamic ...
#'user/*pop*

user=> (alter-var-root #'*pop* (constantly false))
false

user=> *pop*
false