1
votes

I spend a whole day doing research on lexical and dynamic scoping in Clojure (and Lisp). When I thought I finally understood, I made this example and was super surprised that it didn't return what I expected:

(def non-dynamic-var "this is a non dynamic var")
(def ^:dynamic dynamic-var "this is a dynamic var")

(defn function-using-dynamic-var []
  (println dynamic-var))

(defn function-using-non-dynamic-var []
  (println non-dynamic-var))

(defn some-function []

  (function-using-dynamic-var)
  ;; dynamically rebind dynamic-var
  (binding [dynamic-var "this is some new content for the dynamic var"]
    (function-using-dynamic-var))

  (function-using-non-dynamic-var)
  ;; locally rebind non-dynamic-var
  (let [non-dynamic-var "this is some new content that won't be used"]
    (function-using-non-dynamic-var))
  ;; lexically rebind non-dynamic-var
  (def non-dynamic-var "this is some new content that won't be used")
  (function-using-non-dynamic-var))

I'm declaring a normal and a dynamic var, then a respective function that uses each var and then I try what happens if I rebind/redefine the vars. Output from a call of some-function is:

this is a dynamic var
this is some new content for the dynamic var
this is a non dynamic var
this is a non dynamic var
this is some new content that won't be used

Output is as expected except for the last one. I can't get my head around why this would actually output the new value. As I understood lexical and dynamic scoping, this should return still the old value, because what the function function-using-non-dynamic-var sees for non-dynamic-var should still be the definition from Line 1 in my opinion because it's lexical scoping. But apparently I got it wrong, can anybody explain and make sense out of it?

2

2 Answers

1
votes

Okay. in Clojure, all defs are global scope. All. Including the defs here inside your function some-function body. Consequently when executing some-function, the last two forms create a new global definition of non-dynamic-var and then call out to the function using it having modified the global root binding of the var non-dynamic-var.

0
votes

I believe this is due to the way def has been implemented. def creates a Var - "Vars provide a mechanism to refer to a mutable storage location that can be dynamically rebound (to a new storage location) on a per-thread basis." - http://clojure.org/vars. The important thing to note from this Vars are mutable storage locations, and can be rebound.

"If a def expression does not find an interned entry in the current namespace for the symbol being def-ed, it creates one, otherwise it uses the existing Var." - http://clojure.org/vars#Interning

You can see this behavior more simply by running the following:

(def non-dynamic-var "this is a non dynamic var")
(println non-dynamic-var)
(def non-dynamic-var "this is our new value for the the non dynamic var")
(println non-dynamic-var)

This will print:

this is a non dynamic var
this is our new value for the the non dynamic var