Clojure Overloads the Term "Namespace"
Published on Saturday, 2013-08-10Just a quick PSA on a topic I don’t think I’ve heard about anywhere else: the term “namespace” in Clojure.
“Namespace” means at least two things in Clojure, and they often overlap. The first meaning is the one you’re likely thinking of: the namespaces by which we organize code, that vars live in. This is the sort of namespace referred to by the clojure.core
functions and macros that have ns
in the name (ns
, in-ns
, ns-map
, the-ns
, *ns*
, …), as well as several that contain namespace
spelled out. The notable exception is clojure.core/namespace
itself.
user> (doc namespace)
-------------------------
clojure.core/namespace
([x])
Returns the namespace String of a symbol or keyword, or nil if not present.
This actually doesn’t strictly have anything to do with the first meaning of “namespace”. It refers to the part of symbols and keywords preceding the /
if present. E.g.,
user> (namespace :foo)
nil
user> (namespace 'bar)
nil
user> (namespace ::bam)
"user"
user> (namespace :flaz.bang/chizzle)
"flaz.bang"
user> (namespace `taco-sam)
"user"
user> (namespace #'first)
ClassCastException clojure.lang.Var cannot be cast to clojure.lang.Named
user> (namespace second)
ClassCastException clojure.core$second cannot be cast to clojure.lang.Named
What tends to confuse the issue is that though the two don’t have to have anything to do with each other, they often do. For example, if I enter:
user> (clojure.string/trim " HOITS")
"HOITS"
I’ve written code that syntactically contains a symbol with the namespace "clojure.string"
, and the compiler uses this when resolving the symbol to figure out that it needs to look for a var interned under the name trim
in the clojure.string
namespace. But we all know that we may well have done something like this:
(ns user
(:require [clojure.string :as s]))
(s/join " and " ["you" "me" "me" "you"])
;; => "you and me and me and you"
In this case we’re using the symbol s/join
in the code, and even though we know that it’s referring to the #'clojure.string/join
var, the actual namespace of the symbol in the code is not clojure.string
:
(namespace 's/join) ;; => "s"
The compiler is letting us use this symbol even though there is no namespace (in the normal sense) called "s"
.
The namespace
function actually complements the name
function:
(for [x [:foo 'bar :baz.bang/toots 'fizzle/wizzle]]
[x (namespace x) (name x)])
([:foo nil "foo"]
[bar nil "bar"]
[:baz.bang/toots "baz.bang" "toots"]
[fizzle/wizzle "fizzle" "wizzle"])
In summary, both symbols and keywords can have “namespaces” which may or may not match an existing “code-namespace” (and there are uses for all four possibilities).