Cover by Brian Carper, Christophe Grand, Chas Emerick

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

O'Reilly logo

Namespaces

At this point, we should understand much of how the nontrivial parts of the Clojure REPL (and therefore Clojure itself) work:

  • Read: the Clojure reader reads the textual representation of code, producing the data structures (e.g., lists, vectors, and so on) and atomic values (e.g., symbols, numbers, strings, etc.) indicated in that code.

  • Evaluate: many of the values emitted by the reader evaluate to themselves (including most data structures and scalars like strings and keywords). We explored earlier in Expressions, Operators, Syntax, and Precedence how lists evaluate to calls to the operator in function position.

The only thing left to understand about evaluation now is how symbols are evaluated. So far, we’ve used them to both name and refer to functions, locals, and so on. Outside of identifying locals, the semantics of symbol evaluation are tied up with namespaces, Clojure’s fundamental unit of code modularity.

All Clojure code is defined and evaluated within a namespace. Namespaces are roughly analogous to modules in Ruby or Python, or packages in Java.[14] Fundamentally, they are dynamic mappings between symbols and either vars or imported Java classes.

One of Clojure’s reference types,[15] vars are mutable storage locations that can hold any value. Within the namespace where they are defined, vars are associated with a symbol that other code can use to look up the var, and therefore the value it holds.

Vars are defined in Clojure using the def special form, which only ever acts within the current namespace.[16] Let’s define a var now in the user namespace, named x; the name of the var is the symbol that it is keyed under within the current namespace:

(def x 1)
;= #'user/x

We can access the var’s value using that symbol:

x
;= 1

The symbol x here is unqualified, so is resolved within the current namespace. We can also redefine vars; this is critical for supporting interactive development at the REPL:

(def x "hello")
;= #'user/x
x
;= "hello"

Vars are not variables

Vars should only ever be defined in an interactive context—such as a REPL—or within a Clojure source file as a way of defining named functions, other constant values, and the like. In particular, top-level vars (that is, globally accessible vars mapped within namespaces, as defined by def and its variants) should only ever be defined by top-level expressions, never in the bodies of functions in the normal course of operation of a Clojure program.

See Vars Are Not Variables for further elaboration.

Symbols may also be namespace-qualified, in which case they are resolved within the specified namespace instead of the current one:

*ns*                 1
;= #<Namespace user>
(ns foo)
;= nil
*ns*
;= #<Namespace foo>
user/x
;= "hello"
x
;= #<CompilerException java.lang.RuntimeException:
;=   Unable to resolve symbol: x in this context, compiling:(NO_SOURCE_PATH:0)>
1

The current namespace is always bound to *ns*.

Here we created a new namespace using the ns macro (which has the side effect of switching us to that new namespace in our REPL), and then referred to the value of x in the user namespace by using the namespace-qualified symbol user/x. Since we only just created this new namespace foo, it doesn’t have a mapping for the x symbol, so attempting to resolve it fails.

Note

You need to know how to create, define, organize, and manipulate namespaces in order to use Clojure effectively. There is a whole suite of functions for this; please refer to Defining and Using Namespaces for our guidelines in their use.

We mentioned earlier that namespaces also map between symbols and imported Java classes. All classes in the java.lang package are imported by default into each Clojure namespace, and so can be referred to without package qualification; to refer to un-imported classes, a package-qualified symbol must be used. Any symbol that names a class evaluates to that class:

String
;= java.lang.String
Integer
;= java.lang.Integer
java.util.List
;= java.util.List
java.net.Socket
;= java.net.Socket

In addition, namespaces by default alias all of the vars defined in the primary namespace of Clojure’s standard library, clojure.core. For example, there is a filter function defined in clojure.core, which we can access without namespace-qualifying our reference to it:

filter
;= #<core$filter clojure.core$filter@7444f787>

These are just the barest basics of how Clojure namespaces work; learn more about them and how they should be used to help you structure your projects in Defining and Using Namespaces.



[14] In fact, namespaces correspond precisely with Java packages when types defined in Clojure are compiled down to Java classes. For example, a Person type defined in the Clojure namespace app.entities will produce a Java class named app.entities.Person. See more about defining types and records in Clojure in Chapter 6.

[15] See Clojure Reference Types for a full discussion of Clojure’s reference types, all of which contribute different capabilities to its concurrency toolbox.

[16] Remember that the Clojure REPL session always starts in the default user namespace.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required