Clojure

Programming at the REPL: Navigating Namespaces

So far we have only used the REPL for small self-contained experiments; but the REPL is most valuable for putting yourself in the shoes of the program you are developing or debugging, i.e evaluating exactly the same expressions in the REPL as your program does when it runs.

This is achieved by giving your REPL the same context as your running program, which implies using REPL in the same namespaces where your code is defined. We will see how to do that in the following sections.

NOTE: Namespaces are one of the trickiest parts of Clojure. If you’re just learning the language, feel free to skip this chapter for now; you can come back to it when you start working on 'real-world' Clojure projects.

The current namespace

When you evaluate code in the REPL, you are always evaluating code in the context of the current namespace.

The current namespace determines:

  • How the code that you are writing may refer to code from other namespaces.

For example, if the current namespace is myapp.foo.bar and you evaluate (require [clojure.set :as cset :refer [union]]), you can now refer to the clojure.set/union Var either by cset/union (because of the :as cset alias) or just union (because of :refer [union]):

$ clj
Clojure 1.10.0
user=> *ns*
#object[clojure.lang.Namespace 0x7d1cfb8b "user"]
user=> (ns myapp.foo.bar) ;; creating and switching to the myapp.foo.bar namespace - `ns` will be explained later in this guide.
nil
myapp.foo.bar=> (require '[clojure.set :as cset :refer [union]]) ;; this will only affect the current namespace
nil
myapp.foo.bar=> (cset/union #{1 2} #{2 3})
#{1 3 2}
myapp.foo.bar=> (union #{1 2} #{2 3})
#{1 3 2}
myapp.foo.bar=> (cset/intersection #{1 2} #{2 3})
#{2}
myapp.foo.bar=> (in-ns 'user) ;; now switching back to the `user` namespace - `in-ns` will be explained later in this guide.
#object[clojure.lang.Namespace 0x7d1cfb8b "user"]
user=> (union #{1 2} #{2 3})  ;; won't work, because `union` has not been :refer'ed in the `user` namespace
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: union in this context
user=> (cset/intersection #{1 2} #{2 3}) ;; won't work, because the `cset` alias has not been defined in the current namespace.
Syntax error compiling at (REPL:1:1).
No such namespace: cset
user=>

TIP: You can find what aliases are defined in a given namespace by calling ns-aliases.

myapp.foo.bar=> (ns-aliases 'myapp.foo.bar)
{cset #object[clojure.lang.Namespace 0x4b2a01d4 "clojure.set"]}
  • In what namespace the Vars that you define (using for example (def …​) or (defn …​)) will exist.

For example, if the current namespace is myapp.foo.bar and you define a Var named my-favorite-number, you will be able to reference that Var as myapp.foo.bar/my-favorite-number from other namespaces:

$ clj
Clojure 1.10.0
user=> (ns myapp.foo.bar) ;; creating and switching to the `myapp.foo.bar` namespace - NOTE `ns` will be explained later in this guide
nil
myapp.foo.bar=> (def my-favorite-number 42) ;; defining a Var named `my-favorite-number`
#'myapp.foo.bar/my-favorite-number
myapp.foo.bar=> my-favorite-number
42
myapp.foo.bar=> (ns myapp.baz) ;; creating and switching to another namespace `myapp.baz`
nil
myapp.baz=> myapp.foo.bar/my-favorite-number ;; referring to `my-favorite-number`
42
myapp.baz=> (require '[myapp.foo.bar :as foobar]) ;; we can also use an alias to make it shorter
nil
myapp.baz=> foobar/my-favorite-number
42

You can find what the current namespace is by evaluating *ns*:

$ clj
Clojure 1.10.0
user=> *ns*
#object[clojure.lang.Namespace 0x7d1cfb8b "user"]

As you can see, by default, when you start a REPL with clj, the current namespace is user.

Creating a namespace with ns

You can create and switch to a new namespace by evaluating (ns MY-NAMESPACE-NAME):

$ clj
Clojure 1.10.0
user=> (ns myapp.foo-bar)
nil
myapp.foo-bar=> *ns*
#object[clojure.lang.Namespace 0xacdb094 "myapp.foo-bar"]
myapp.foo-bar=> (def x 42)
#'myapp.foo-bar/x

Note: when you switch to a new namespace, the names and aliases that were defined in the previous namespaces are no longer available:

$ clj
Clojure 1.10.0
user=> (ns myapp.ns1) ;; creating a new namespace and defining a Var `x` and an alias `str/`:
nil
myapp.ns1=> (def x 42)
#'myapp.ns1/x
myapp.ns1=> x
42
myapp.ns1=> (require '[clojure.string :as str])
nil
myapp.ns1=> (str/upper-case "hello")
"HELLO"
myapp.ns1=> (ns myapp.ns2) ;; now switching to another namespace:
nil
myapp.ns2=> x ;; won't work, because x has not been defined in namespace `myapp.ns2`
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: x in this context
myapp.ns2=> (str/upper-case "hello") ;; won't work, because alias `str` has not been defined in namespace `myapp.ns2`
Syntax error compiling at (REPL:1:1).
No such namespace: str

Switching to an existing namespace with in-ns

You can switch to an existing namespace by evaluating (in-ns 'MY-NAMESPACE-NAME). Here’s an example REPL session that creates a namespace myapp.some-ns, defines a Var named x in it, moves back to the user namespace, then moves again to myapp.some-ns:

$ clj
Clojure 1.10.0
user=> (ns myapp.some-ns) ;;;; creating the namespace `myapp.some-ns`
nil
myapp.some-ns=> *ns* ;; where are we?
#object[clojure.lang.Namespace 0xacdb094 "myapp.some-ns"]
myapp.some-ns=> (def x 42) ;; defining `x`
#'myapp.some-ns/x
myapp.some-ns=> (in-ns 'user) ;;;; switching back to `user`
#object[clojure.lang.Namespace 0x4b45dcb8 "user"]
user=> *ns* ;; where are we?
#object[clojure.lang.Namespace 0x4b45dcb8 "user"]
user=> (in-ns 'myapp.some-ns) ;;;; ...switching back again to `myapp.some-ns`
#object[clojure.lang.Namespace 0xacdb094 "myapp.some-ns"]
myapp.some-ns=> *ns* ;; where are we?
#object[clojure.lang.Namespace 0xacdb094 "myapp.some-ns"]
myapp.some-ns=> x ;; `x` is still here!
42

What happens if you in-ns to a namespace that has never been created? You will see strange things happening. For instance, you will not be able to define a function using defn:

$ clj
Clojure 1.10.0
user=> (in-ns 'myapp.never-created)
#object[clojure.lang.Namespace 0x22356acd "myapp.never-created"]
myapp.never-created=> (defn say-hello [x] (println "Hello, " x "!"))
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: defn in this context

Explanation: in this situation, in-ns creates the new namespace and switches to it like ns does, but it does a little less work than ns, because it does not automatically make available all the names defined in clojure.core, such as defn. You can fix that by evaluating (clojure.core/refer-clojure):

myapp.never-created=> (clojure.core/refer-clojure)
nil
myapp.never-created=> (defn say-hello [x] (println "Hello, " x "!"))
#'myapp.never-created/say-hello
myapp.never-created=> (say-hello "Jane")
Hello,  Jane !
nil

If you only use in-ns to switch to namespaces that have already been created, you won’t have to deal with these subtleties.

Working with libs

Most of the namespaces you will navigate at the REPL will already exist in source files or dependencies of your project, i.e in libs of your project.

There is an important usage precaution for switching to namespaces defined in libs:

If a namespace is defined in a lib of your project, always make sure you have loaded the lib in the REPL before switching to it.

How to make sure a lib is loaded

To make sure that a lib with namespace mylib.ns1 has been loaded in the REPL, you can do any one of the following:

  1. require it directly: (require '[mylib.ns1])

  2. load a namespace which itself requires mylib.ns1 (directly or indirectly).

  3. evaluate manually all the code in the source file mylib.ns1

Example: a project for greeting people

For example, assume a Clojure project with the following structure and content:

.
└── src
    └── myproject
        ├── person_names.clj
        └── welcome.clj
;; -----------------------------------------------
;; src/myproject/welcome.clj
(ns myproject.welcome
  (:require [myproject.person-names :as pnames])) ;; NOTE: `myproject.welcome` requires `myproject.person-names`

(defn greet
  [first-name last-name]
  (str "Hello, " (pnames/familiar-name first-name last-name)))


;; -----------------------------------------------
;; src/myproject/person_names.clj
(ns myproject.person-names
  (:require [clojure.string :as str]))

(def nicknames
  {"Robert"     "Bob"
   "Abigail"    "Abbie"
   "William"    "Bill"
   "Jacqueline" "Jackie"})

(defn familiar-name
  "What to call someone you may be familiar with."
  [first-name last-name]
  (let [fname (str/capitalize first-name)
        lname (str/capitalize last-name)]
    (or
      (get nicknames fname)
      (str fname " " lname))))

Here are 3 ways to make sure myproject.person-names is loaded:

$ clj ## APPROACH 1: requiring myproject.person-names directly
Clojure 1.10.0
user=> (require '[myproject.person-names])
nil
user=> myproject.person-names/nicknames ;; checking that the myproject.person-names was loaded.
{"Robert" "Bob", "Abigail" "Abbie", "William" "Bill", "Jacqueline" "Jackie"}
$ clj ## APPROACH 2: requiring myproject.welcome, which itself requires myproject.person-names
Clojure 1.10.0
user=> (require '[myproject.welcome])
nil
user=> myproject.person-names/nicknames ;; checking that the myproject.person-names was loaded.
{"Robert" "Bob", "Abigail" "Abbie", "William" "Bill", "Jacqueline" "Jackie"}
$ clj ## APPROACH 3: manually copying the code of myproject.person-names in the REPL.
Clojure 1.10.0
(ns myproject.person-names
  (:require [clojure.string :as str]))

(def nicknames
  {"Robert"     "Bob"
   "Abigail"    "Abbie"
   "William"    "Bill"
   "Jacqueline" "Jackie"})

(defn familiar-name
  "What to call someone you may be familiar with."
  [first-name last-name]
  (let [fname (str/capitalize first-name)
        lname (str/capitalize last-name)]
    (or
      (get nicknames fname)
      (str fname " " lname))))
nil
myproject.person-names=> myproject.person-names=> #'myproject.person-names/nicknames
myproject.person-names=> myproject.person-names=> #'myproject.person-names/familiar-name
myproject.person-names=> myproject.person-names/nicknames ;; checking that the myproject.person-names was loaded.
{"Robert" "Bob", "Abigail" "Abbie", "William" "Bill", "Jacqueline" "Jackie"}

TIP: you can see (among other things) what libs get loaded by using the :verbose tag in require:

$ clj
Clojure 1.10.0
user=> (require '[myproject.welcome] :verbose)
(clojure.core/load "/myproject/welcome")
(clojure.core/in-ns 'clojure.core.specs.alpha)
(clojure.core/alias 's 'clojure.spec.alpha)
(clojure.core/load "/myproject/person_names")
(clojure.core/in-ns 'myproject.person-names)
(clojure.core/alias 'str 'clojure.string)
(clojure.core/in-ns 'myproject.welcome)
(clojure.core/alias 'pnames 'myproject.person-names)
nil

How things can go wrong

Continuing with the above example project, here is a REPL session showing how things can go wrong if you switch to a lib namespace without loading it first:

$ clj
Clojure 1.10.0
user=> (ns myproject.person-names)
nil
myproject.person-names=> nicknames ;; #'nicknames won't be defined, because the lib has not been loaded.
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: nicknames in this context
myproject.person-names=> (require '[myproject.person-names]) ;; won't fix the situation, because the namespaces has already been created
nil
myproject.person-names=> nicknames
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: nicknames in this context