What namespaces Clojure uses for def-ing

103 Views Asked by At

According to spec, def should intern the var in the current ns (i.e. *ns*). However, the following code does not look anything like it:

(ns namespace-b)

(defn def_something []
  (ns namespace-a)
  (println *ns*) ;prints namespace-a as it should
  (def something 1)
) 

(def_something)

(println namespace-b/something) ; prints 1 
(println namespace-a/something) ; throws

What am I missing?

Notes:

  • defn is used just for clarity. Defining and running anonymous function works just as well.
  • I know that using def inside function is probably not very idiomatic. However, this is just extracted essence of a bigger problem I ran into.
2

There are 2 best solutions below

5
On BEST ANSWER

The parser already interns the var to the current namespace at compile time, although it won't be bound immediately:

(defn dd [] (def x 0))
x ;; => #<Unbound Unbound: #'user/x>

The relevant piece of code can be found here, with the second parameter to lookupVar triggering the aforementioned interning for non-existing vars here.

The parses then generates an expression that references the previously created var, so the expression logic never leaves the current namespace.

TL;DR: def is something that the compiler handles in a special kind of way.

0
On

The key thing to understand about def is that it is a macro. This means that it does not resolve the namespace or create the binding at runtime, but beforehand, while the code is being compiled.

If you call a function that calls def, that call to def was already resolved to use the namespace in which the function was defined. Similarly, if you call functions inside a function body, the functions to call are resolved at compile time within the namespace where that function was defined.

If you want to generally bind values to namespaces at runtime, you should use the function intern, which lets you explicitly set the namespace to mutate.

All this said, namespace mutation is just that, it's procedural and is not thread safe and does not have nice declarative semantics like other options Clojure makes available. I would strongly suggest finding a way to express your solution that does not involve unsafe runtime mutation.