clojuredynamic-scope

clojure and ^:dynamic


I tried to understand dynamic variables and binding function so I tried this (clojure 1.3):

user=> (defn f [] 
           (def ^:dynamic x 5) 
           (defn g [] (println x)) 
           (defn h [] (binding [x 3] (g))) 
           (h))
#'user/f
user=> (f)     
5
nil

Confused, I tried this somewhat simpler code:

user=> (def ^:dynamic y 5)
#'user/y
user=> (defn g [] (println y))
#'user/g
user=> (defn h [] (binding [y 3] (g)))
#'user/h
user=> (h)
3
nil

What is the difference between the two pieces of code? Why does the second example work but the first does not?

Hint: I just realized that the following works (still don't fully understand why):

user=> (def ^:dynamic y 5)
#'user/y
user=> (defn f [] (defn g [] (println y)) (defn h [] (binding [y 3] (g))) (h))
#'user/f
user=> (f)
3
nil
user=> 

Solution

  • I get 3 as a result (as you would expect) when I run your first example in Clojure 1.4.... have you tried this with a fresh REPL?

    ^:dynamic is an instruction to the Clojure compiler that a symbol (as defined with def) is intended to be dynamically rebound (with binding).

    Example:

    (def foo 1)
    (binding [foo 2] foo)
    => IllegalStateException Can't dynamically bind non-dynamic var: ...
    
    (def ^:dynamic bar 10)
    (binding [bar 20] bar)    ;; dynamically bind bar within the scope of the binding
    => 20
    bar                       ;; check underlying value of bar (outside the binding)
    => 10
    

    Note that binding has dynamic scope within the calling thread - any functions called within the binding will see the modified value of bar (20), but any other threads will still see the unchanged root value of 10.

    Finally a couple of style points that you may find helpful: