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=>
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 withdef
) is intended to be dynamically rebound (withbinding
).Example:
Note that
binding
has dynamic scope within the calling thread - any functions called within the binding will see the modified value ofbar
(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:
def
anddefn
within functions as they affect the enclosing namespace. Within functions you should use(let [foo bar] ...)
instead.binding
you should normally consider whether you can achieve the same result using higher order functions instead.binding
is useful in some contexts but it is not in general a good way to pass parameters around - function composition is usually better in the long run. The reason for this is thatbinding
creates an implicit context that is required for the execution of your function and this can be difficult to test/debug.