Welcome! Please see the About page for a little more info on how this works.

0 votes
in Namespaces and vars by
edited by

We know that when we send a symbol to REPL, Clojure will resolve the symbol to var, and get the value which is currently the var's bindings. And we also know that var object is stable when it is interned by namespace, so we can dynamically modify the runtime behavior of a program(by re-def-ing the var's bindings).
But here comes a strange things that I don't understand.
Screenshot of the REPL process

user=> (ns ns-a)
ns-a=> (def a 42)
ns-a=> a
;;42
ns-a=> (ns ns-b)
ns-b=> (refer 'ns-a)
ns-b=> a
;;42
ns-b=> (var a)
#'ns-a/a
ns-b=> (in-ns 'ns-a)
ns-a=> a
;;42
ns-a=> (def a 99)
ns-a=> a
;;99
ns-a=> (in-ns 'ns-b)
ns-b=> a
;;99
;;nothing special so far
ns-b=> (in-ns 'ns-a)
ns-a=> (ns-publics *ns*)
;;{a #'ns-a/a}
;;see, it's interned
ns-a=> (ns-unmap *ns* 'a)
;;lets unmap the (var a) from ns-a
ns-a=> (ns-publics *ns*)
;;{}
ns-a=> a
;;Syntax error compiling at (REPL:0:0).
;;Unable to resolve symbol: a in this context
ns-a=> (var a)
;;Syntax error compiling var at (REPL:1:1).
;;Unable to resolve var: a in this context
;;(var a) is nowhere.
ns-a=> (in-ns 'ns-b)
ns-b=> a
;;99
;;the referred symbol a still evaluates to 99.
ns-b=> (var a)
;;#'ns-a/a
;;yeah, (var a) is still somewhere.
ns-b=> (deref (var a))
;;#object[clojure.lang.Var$Unbound 0x4a68135e "Unbound: #'ns-a/a"]
;;What? (var a) is being unbound. Is this a new (var a)?
ns-b=> a
;;99
;;it is not resolved from (var a)?
ns-b=> (in-ns 'ns-a)
ns-a=> a
;;#object[clojure.lang.Var$Unbound 0x4a68135e "Unbound: #'ns-a/a"]
;;What? it comes back?
ns-a=> (def a 53)
;;Lets redefine (var a) to bind 53
ns-a=> a
;;53
ns-a=> (in-ns 'ns-b)
;;Lets switch back and check.
ns-b=> a
;;99
;;not changed
ns-b=> (deref (var a))
;;53
;;so I'm sure 99 it is not resolved from (var a).

The first question: where is the value 99 resides? It used to being resolved from #'a, but it seems being like a lexical bindings after #'a being unmapped. Is it?
The second question: why the #'a is interned back with unbound state after execute (var a) in another namespace(which has a refering to #'a)? Seems it create a new var to fullfill the state.

1 Answer

+2 votes
by

It might be helpful to step back to concepts a bit. A namespace holds a set of names (symbols) mapped to vars. A var is a box for a value. def maps a name to a var in the current namespace. refer maps the name in the current namespace to the var in a different namespace.

Q1: ns-unmap removes the mapping in ns-a from symbol to var, but it does not destroy the var. The var still exists (and is referenced by the refer in ns-b).

Q2: var is a special form (implemented in the compiler) and is maybe doing something different than what you think. var will resolve to the var object (here the symbol a in ns-b resolves to the var at compile time which is #'ns-a/a (not the var object, but this token!!). That is then evaluated by looking up the var for a in ns-a (which does not exist). I think it's this special form compilation that's the confusing bit here (and only happens due to var).

...