Despite several good texts that attempted to explain them (including the outstanding Clojure for the Brave and True), I had until recently a fuzzy understanding of how namespaces (object naming in general, really) work in Clojure. I finally bit the bullet and dug into the surprisingly readable source code for Clojure and sorted things out for myself.
To start at the global scope and drill down: the Clojure runtime maintains a map from symbols to namespaces. I don’t see a way to expose this directly as a map within Clojure itself, but
all-ns will iterate over the namespaces. You can then use
ns-name to get the symbolic name for a namespace if you need it.
The current namespace is determined by the value of the dynamic Var clojure.core/*ns*.
As it turns out, namespaces are surprisingly simple. A namespace is just a pair of maps: one from symbols to Vars and classes (the namespace map, returned by
ns-map), and one from symbols to namespaces (the alias map, returned by
ns-aliases). The namespace map is what refers to the objects contained in the namespace; e.g., if you enter
foo/bar (or just
foo is your current namespace) in your code or at the REPL, the evaluator looks in
foo‘s namespace map for a Var or class associated with the symbol
bar. The alias map provides aliases for one or more namespaces within another namespace. E.g., if namespace
foo has an entry in its alias map from the symbol
b to the namespace
foo is the current namespace,
b/bar will be equivalent to
One detail that I hadn’t been clear on before: Vars contain a reference to the namespace in which they are interned, as well as the symbol with which they are initially associated. Namespaces themselves do not directly track which Vars are interned in them; if you call
ns-interns, it just filters the Var values in the namespace map to only include those that claim the namespace in question as the one in which they are interned. The symbol that the Var gets when it is created is as far as I can tell only used for documentation, not identification. If
foo is interned into namespace
ns1, referred into namespace
bar, and then removed from
ns-unmap, the Var returned will still call itself
#'ns1/foo, even though it is only accessible as