13

I could not set my dynamic var's value to new one.

(def *pop* true)

(set! *pop* false)

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


Also I've added ^:dynamic, which did not work either.

(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)


But on the other hand this code works,(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
Ertuğrul Çetin
  • 5,131
  • 5
  • 37
  • 76

2 Answers2

19

Dynamic vars can only be set! inside a binding scope. So just calling set! on *pop* won't work - you need to be in the runtime dynamic scope of a binding somewhere in the call stack above.

(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)

Note that the "dynamic scope" part of it means that within binding you can make arbitrary nested calls and still have access to set and read the per-thread value of *pop*.

Regarding *warn-on-reflection*, this looks like special behavior but it's actually exactly the same, except hidden from view. The REPL itself creates a dynamic binding scope around the eval of each REPL statement with bindings for a hard-coded set of dynamic vars, of which *warn-on-reflection* is one. You can find that set of bindings here.

Alex Miller
  • 69,183
  • 25
  • 122
  • 167
6

You can use alter-var-root to change the root vars.

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

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

user=> *pop*
false
ymonad
  • 11,710
  • 1
  • 38
  • 49