Ertuğrul Çetin
Ertuğrul Çetin

Reputation: 5231

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

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

Upvotes: 13

Views: 2886

Answers (2)

ymonad
ymonad

Reputation: 12120

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

Upvotes: 6

Alex Miller
Alex Miller

Reputation: 70239

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.

Upvotes: 19

Related Questions