Namespaces

Create a new namespace:

(ns music)

The new namespace is activated as the current namespace, which will hold the vars in a lookup table indexed by their symbols.

Define vars with the same name in two different namespaces:

(ns iron-maiden)
(def first-album "Iron Maiden")

(ns fates-warning)
(def first-album "Night on Bröcken")

(ns iron-maiden)
(println first-album) ; "Iron Maiden"

(ns fates-warning)
(println first-album) ; "Night on Bröcken"

Using ns on an existing namespace does not create a new namespace, but activates the existing one.

Use fully qualified symbols to access other namespaces:

(ns music-shop)
(println iron-maiden/first-album) ; "Iron Maiden"
(println fates-warning/first-album) ; "Night on Bröcken"

Use a namespace from the standard library, and use it to compare data objects:

(require 'clojure.data)

(clojure.data/diff {:song "The Apparition" :band "Iron Maiden" :duration "5m50s"}
                   {:song "The Apparition" :band "Fates Warning" :duration "3m54s"})
;; ({:duration "5m50s", :band "Iron Maiden"}
;;  {:duration "3m54s", :band "Fates Warning"}
;;  {:song "The Apparition"})

The diff function returns a three-element list with

  1. data that only exists in the first argument,
  2. data that only exists in the second argument, and
  3. data that is common to both arguments.

Create a new Leiningen project in the shell:

lein new app music_store

The file music_store/src/music_store.clj defines the namespace music-store.core and contains the following code:

(ns music-store.core
  (:gen-class))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

Dashes are used for symbol names, and underscores for file names.

Create a new namespace music-store.bands in music_store/src/music_store/bands.clj:

(ns music-store.bands)

(def iron-maiden {:name "Iron Maiden" :founded 1975})
(def fates-warning {:name "Fates Warning" :founded 1983})

Use a namespace in the ns declaration in music_store/src/mustic_store/core.clj:

(ns music-store.core
  (:require music-store.bands))

Unlike the standalone require function used in the REPL, :require declarations within ns do not need quoting:

(:require music-store.bands) ; within ns: no quoting
(require 'music-store.bands) ; from the REPL: quoting

Create a (shorter) alias for a namespace:

(require '[music-store.bands :as bands])

Same within ns without quoting:

(ns music-store.core
  (:require [music-store.bands :as bands]))

Pull in specific symbols from another namespace:

(require '[music-store.bands :refer [fates-warning]])
(println fates-warning) ; {:name Fates Warning, :founded 1983}

Pull in all the symbols from another namespace:

(require '[music-store.bands :refer :all])
(println iron-maiden) ; {:name Iron Maiden, :founded 1975}

Use :refer :all with caution, for it pollutes the current namespace.

Get hold of the current namespace:

(println *ns*) ; #namespace[music-store.core]

Lookup a namespace by its name:

(find-ns 'music-store.bands) ; #namespace[music-store.bands]

Discover the bindings of a namespace:

(ns-map (find-ns 'music-store.bands))

If a symbol instead of a namespace is given, ns-map will look up the namespace:

(ns-map 'music-store.bands)

Get the namespace of a symbol:

(namespace 'music-store.bands/fates-warning) ; "music-store.bands"

Qualify a keyword with a namespace:

:music-store.bands/iron-maiden

Qualify a keyword with the current namespace (e.g. from music-store.core):

::whatever ; :music-store.core/whatever

Reload the symbols of a namespace (force-reloads already loaded symbols):

(require :reload '[music-store.bands])

Get rid of loaded symbols:

(ns-unmap 'music-store.bands 'iron-maiden)

Define a var that resits :reload (e.g. when initialized using a heavy function call):

(defonce fib-42 (fib 42))

Exercises

Leiningen Project

Create a new Leiningen project called fibonacci and run the generated code.

Hint: Use lein run to run the project from the fibonacci/ folder.

Test: lein run shall output "Hello, World!".

Solution
lein new app fibonacci
cd fibonacci
lein run

Additional Namespace

Create a new namespace recursive within the fibonacci project. Implement a function called fib that calculates the nth Fibonacci number given the parameter n. Use that function from the core namespace (src/fibonacci/core.clj) in the -main function and call it with the argument 35 and output the result.

Hint: Put the file into the src/fibonacci folder and name it according to the namespace defined therin.

Test: The application shall output fib(35)=14930352.

Solution

src/fibonacci/recursive.clj:

(ns fibonacci.recursive)

(defn fib [n]
  (if (<= n 1)
    1
    (+ (fib (- n 2)) (fib (- n 1)))))

src/fibonacci/core.clj:

(ns fibonacci.core
  (:require fibonacci.recursive)
  (:gen-class))

(defn -main
  [& args]
  (println (str "fib(35)=" (recursive/fib 35))))

Yet Another Namespace

Create a new namespace tail-recursive within the fibonacci project. Re-use the tail-recursive implementation of the fib function from chapter 5.

Now import both the recursive and tail-recursive namespaces into the core namespace as fib-rec and fib-tail, respectively. Call both functions with the argument 35 and output the result as before.

Hint: Use :require and :as to define an alias name for the namespaces.

Test: The application shall output fib(35)=14930352 twice.

Solution

src/fibonacci/tail_recursive.clj:

(ns fibonacci.tail-recursive)

(defn fib [n]
  (loop [a 1
         b 1
         i n]
    (if (= i 0)
      a
      (recur b (+ a b) (- i 1)))))

src/fibonacci/core.clj:

(ns fibonacci.core
  (:require [fibonacci.recursive :as fib-rec])
  (:require [fibonacci.tail-recursive :as fib-tail])
  (:gen-class))

(defn -main
  [& args]
  (println (str "fib(35)=" (fib-rec/fib 35)))
  (println (str "fib(35)=" (fib-tail/fib 35))))