Scoping rules in Clojure
clojure
Solution
`let` shadows the toplevel Var `x` with a local `x`. `let` does not create a Var or affect the toplevel Var; it binds some symbol such that local references to that symbol will be replaced with the `let`-bound value. `let` has lexical scope, so its bindings are only visible within the `let` form itself (not in functions called from within the `let`).
`binding` temporarily (thread-locally) changes the value of the toplevel Var `x`, that's all it does. If a `let` binding is in place, `binding` doesn't see it when deciding which value to change (and `let`'s bindings are not vars and are not alterable, so that' a good thing or it'd give you an error). And `binding` won't mask `let`. `binding` has dynamic scope, so its affects on toplevel Vars are visible within the `binding` form and in any function that is called from within the `binding` form.
Accessing the value of plain old `x` will give you whatever is at the top of the stack of bindings, either the most nested `let`-bound value of `x` (or the function paramater called `x`, or some value `x` is replaced with if you use your own macro, or other possibilities.), and only uses the current value of the toplevel Var `x` by default if there is no other binding in place.
Even if the toplevel Var `x` is masked by a `let`-bound `x`, you can always access the toplevel Var via `@#'x`. Try this version, maybe it'll make more sense:
(def x 1)
(defn dummy-fn2[]
(println "x from dummy-fn2:" x)
(+ x 1))
(defn dummy-fn[]
(println "entering function:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---")
(let [x 100]
(println "after let:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---")
(let [x (dummy-fn2)]
(println "after let and dummy-fn2:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---")
(binding [x 888]
(println "after binding:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---")
(let [x (dummy-fn2)]
(println "after binding and dummy2:" x)
(println "var x:" @#'x)
(dummy-fn2)
(println "---"))))))
Gives:
entering function: 1
var x: 1
x from dummy-fn2: 1
---
after let: 100
var x: 1
x from dummy-fn2: 1
---
x from dummy-fn2: 1
after let and dummy-fn2: 2
var x: 1
x from dummy-fn2: 1
---
after binding: 2
var x: 888
x from dummy-fn2: 888
---
x from dummy-fn2: 888
after binding and dummy2: 889
var x: 888
x from dummy-fn2: 888
---
Problem
Even though I have used Clojure, I hadn't looked at the scoping rules in detail. I am getting more confused as I read the documentations. I made a small test to try out the scoping resolutions and am apalled at the complexity. Could somebody explain the intent and various rules that Clojure uses? ``` (def x 1) (defn dummy-fn2[] (+ x 1)) (defn dummy-fn[] (println "entering function: " x) (let [x 100] (println "after let: " x) (let [x (dummy-fn2)] (println "after let and dummy2: " x) (binding [x 100] (println "after binding: " x) (let [x (dummy-fn2)] (println "after binding and dummy2: " x)))))) 1:2 foo=> (dummy-fn) entering function: 1 after let: 100 after let and dummy2: 2 after binding: 2 after binding and dummy2: 101 nil ```