Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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=>  
like image 407
Kevin Avatar asked Jul 30 '12 22:07

Kevin


People also ask

Why is Clojure dynamic?

One of the reasons to keep Clojure (and any Lisp) dynamically typed is to simplify creation of macros. In short, macros deal with abstract syntax trees (ASTs) which can contain nodes of many, many different types (usually, any objects at all).

Is Clojure compiled or interpreted?

Clojure is a compiled JVM language. That means that the first step it takes when confronted with a new program is to compile it to JVM bytecode.

What is Var in Clojure?

Advertisements. In Clojure, variables are defined by the 'def' keyword. It's a bit different wherein the concept of variables has more to do with binding. In Clojure, a value is bound to a variable.

What is binding in Clojure?

;; binding creates a dynamically scoped binding for some Var. ;; Dynamic binding means that the code inside your binding form and any code ;; which that code calls (even if not in the local lexical scope) will see the new binding.


1 Answers

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:

  • It's generally considered bad idea to put def and defn within functions as they affect the enclosing namespace. Within functions you should use (let [foo bar] ...) instead.
  • When you find yourself wanting to use 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 that binding creates an implicit context that is required for the execution of your function and this can be difficult to test/debug.
like image 56
mikera Avatar answered Sep 21 '22 00:09

mikera