May 12

Namespaces survival kit

When learning Clojure there are so many interesting things to get your hands on that you forget to master the basics. It happened to me with the namespaces. In this post I’ll try to revise the API for operating namespaces and show some useful techniques.

All the code in Clojure is organized into namespaces. Stuff from one subjectively defined scope goes here, stuff from the other goes there – simple as that. The “stuff” consists of functions, macros, protocols definitions, records and any other fancy language features we enjoy every day. But the true beauty is that the good enough model to think about namespaces is a map of symbols and vars.

REPL – When in Rome…

Clojure is famous for its interactive development in REPL. So, when you are in REPL the most convenient tools should be used. Let’s say I’ve created a new project with lein new aloha and started it with lein repl – by default I end up in user namespace. To confirm in which namespace I’m actually in I may check the value of *ns* (it tells us in what context the code is executed, at runtime):

user=> *ns*
#<Namespace user>

Now I simply create namespace with ns:

user=>(ns testns)

I’m still in user namespace (just go and check the value of *ns*). To switch to the newly created namespace use in-ns:

user=> (in-ns ‘testns)
#<Namespace testns>

You may have read in documentation that in-ns creates a namespace if it doesn’t yet exist so using ns macro might sound like an overkill. However, ns in spite of in-ns automatically makes namespace implicitly refer to core clojure library. Try creating a namespace with in-ns and see that you can’t do anything useful in it (e.g. basic addition of numbers doesn’t work). BTW ns also automatically quotes a namespace name so it’s even more convenient to use instead of in-ns.

user=> (in-ns ‘testns2)
#<Namespace testns2>
user=>(+ 1 1)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: + in this context, compiling:(NO_SOURCE_PATH:1)

At this point very often the require, use, refer might be called to use stuff from other namespaces. Check examples of usage on ClojureDocs simply by searching for a function name.

Now it is time to define something useful in the namespace:

=> (def a "Dummy string")
   (defn dummy-foo [] "Dummy foo return value")

After a second I decided that a is not really needed and should be unmapped to avoid clutter. In fact the whole namespace is pretty useless and can be removed :)

=> a
"Dummy string"
=> (ns-unmap 'testns 'a)
=> a
CompilerException java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(NO_SOURCE_PATH:1)
=>(remove-ns 'testns)
#<Namespace testns>
;don’t call *ns* now, nrepl for example will present you with an ugly NullPointerException :)

This is how to manipulate the namespaces in REPL. Let’s move to the situation when you’re working on the code in file.

When back in code…

Simply use ns. It is the most convenient and declarative way of defining namespace. Below is a template presenting some features that might be used:

(ns foo.bar
  (:refer clojure :exclude [ancestors printf])
  (:require (clojure.contrib sql sql.tests))
  (:use (my.lib this that))
  (:import (java.util Date Timer Random)
           (java.sql Connection Statement)))

To have a better understanding of what is possible to do check the Related functions section from the official documentation.

Thanks for reading!

Leave a Reply

Your email address will not be published.