Clojure Overloads the Term "Namespace"

Published on Saturday, 2013-08-10

Just 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)
  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)
user> (namespace 'bar)
user> (namespace ::bam)
user> (namespace :flaz.bang/chizzle)
user> (namespace `taco-sam)
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")

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).