Now that we know how to create our functions we need to understand how Clojure organizes and accesses it, this way is called namespaces.
Every good wizard has a lot of books on his bookshelf, with different magics to use in a lot of situations. Imagine a Clojure namespace as being one of your books, and each function or variable (defined with
def) is written on it, when you need to use your function, you just search for it in this book and use it!
If you enter in your REPL, you’ll be in a “default” namespace called
user, everything that you define here, we’ll be associated with this namespace.
user => (def author-name "otavio") #'user/author-name
You may have already noticed that when we defined
author-name it returned
#'user/author-name, it means that you defined an
author-name in the user namespace.
To create a new namespace and switch to it, you only need to type
ns and a name to it, it will create (if not exists), and you can’t access values defined in other namespace directly anymore. Just like a blank book, waiting to be written.
user => (ns my-new-ns) nil my-new-ns => author-name Syntax error compiling at (REPL:1:1). Unable to resolve symbol: author-name in this context
If you want, you can access any variable or function defined in other namespace, just typing its “full address” namespace + name.
my-new-ns => user/author-name "otavio"
Switch between namespaces is easy, just use
in-ns and pass the namespace name preceded by quote. After it, you can access the defined values of this namespace directly again.
any-ns=> (in-ns 'user) #object[clojure.lang.Namespace 0x56317ffa "user"] user=> author-name "otavio"
You can always see in which namespace are you, just typing
*ns* (but you can just see in your REPL too.)
Reading other books
Sometimes you as a wizard will want to read books written by other wizards, to not lose your precious time reinventing the wheel creating already existent magics.
Most languages are shipped with a lot of code that you must need, things like string operations, basic http logic, and much more. All this code is available for you, in the form of namespaces, and to use it is simple!
Every time that you create a new namespace using
ns in your REPL, it will be automatically loaded on it, and you can just start using it, but this is not true while trying to access namespaces packaged in files (which will be important in the future when we start to work with multiple files in large projects).
Let’s use as an example the namespace
clojure.string which contains functions to deal with common string operations, we want to use
capitalize function from it! The first step is to make our REPL aware of the namespace that you want to use using the
require function, passing as an argument the namespace that we want to load in our REPL, after this all functions and symbols globally defined in this namespace are available to us, just use them using its ‘full address’
namespace + name.
user => (require 'clojure.string) nil user => (clojure.string/capitalize "hello clojure") "Hello lojure"
clojure.string every time isn’t productive, so we can use the
:as keyword to create an alias to use this namespace.
user => (require '[clojure.string :as str]) nil user => (str/capitalize "hello clojure") "Hello clojure"
This required namespace will be available only on current namespace, if you change to other namespace, it will no longer be available.
Better now, ya? This is the Clojurist way of using external namespaces from other namespace (including libraries as we’ll see in the future).
If you want, you can also enter in the string namespaces and use functions directly, but I really don’t know why you may want to do it:
user => (require 'clojure.string) nil user => (in-ns 'clojure.string) nil clojure.string => (capitalize "hello clojure") "Hello clojure"
Learn how to use namespaces is important! We did not learn how to pack namespace in files yet, but we will in the future! This knowledge is important, it will help while developing using our REPL, and in the future post talking about files.
On the next post of this series, we’ll still talking about functions, with just a short trick to make our functions better and readable!
If you have any questions that I can help you with, please ask! Send an email (otaviopvaladares at gmail.com), pm me on my Twitter, or comment on this post!
The mindset in clojure is that you can navigate namespaces as you would directories, so you set the curr dir with in-ns, list available symbols in there with ns-map etc. https://clojure.org/reference/namespaces#_related_functions
Very interesting! I’ll write more about this mindset in a future post talking about Clojure’s development environment, it will help a lot! Thanks for your feedback