Chapter 1. The Structure of Clojure
Hello there. Do you have your Clojure REPL ready? If not, be sure to flip back to the Preface for information on getting it up and running so that you can explore the code examples as we go along in this chapter.
Beginning is always the hardest part. There are so many wonderful parts to Clojure that it is hard to figure out where to begin. When in doubt, always start simple. This chapter is about understanding the basic structure of a Clojure expression. So what is the simplest thing that we can enter into our Clojure REPL as code?
How about an integer?
Let’s try it together. Type your favorite integer into your REPL and see what happens:
42
;; -> 42
Here’s a rundown of what’s going on here:
-
The number 42 was typed into the REPL and the Enter key was hit.
-
The result of the expression was itself (meaning that it self-evaluated to 42).
-
The result of the expression was printed out. Here, we are showing that it is printed out by using the prefix
;; →
.
In this simplest example, we have seen the basic mechanics of Clojure code evaluation. Clojure code is made up of expressions that are then evaluated to return a result. The simplest of these expressions are those that evaluate to themselves. They are called simple values or literals.
Baby Steps with Simple Values
We can type other kinds of Clojure code into the REPL that will also evaluate to itself. We have done an integer, so let’s try a decimal:
12.43
;; -> 12.43
1
/3
;; -> 1/3
Notice that when it was evaluated, it didn’t get changed to a decimal. This is pretty nice, because we don’t have to worry about truncation of decimals until we absolutely need to. If the ratio of the two integers can be reduced, it will be, like in this example:
4
/2
;; -> 2
Note that this can only be done with integers. Watch what happens when we try with a decimal:
4.0
/2
;; -> NumberFormatException Invalid number: 4.0/2
If we want to actually do some math, we will need a bigger expression—one with parens. Because we were just doing ratios, let’s try to divide 1 by 3:
(
/
1
3
)
;; -> 1/3
Of course, you probably noticed the parens. The other thing that makes this different from other languages is that the operator goes first, followed by the values that you want to divide. This is that basic structure of Clojure code. The function or operator goes first, then the parameters that it needs. Also notice that the result is a ratio value.
Note
In Clojure, the function or operator goes first, followed by the parameters that it needs.
Let’s return to our division example and do it again, this time with a decimal:
(
/
1
3.0
)
;; -> 0.3333333333333333
The result here is a decimal because we started out with one of the arguments being a decimal.
Let’s take a step back from math and numbers, and talk about some other simple values that evaluate to themselves, like strings.
Are you hungry? How about typing "jam"
, into your REPL?
"jam"
;; -> "jam"
Strings in Clojure are fun and easy—you just need to put quotes around some text.
Keywords are symbolic identifiers in Clojure whose names start with a colon. They have special properties that make them very useful in ways that we will explain later on. They are simple values, too—try jam as a keyword by putting a colon right in front of the letters:
:jam
;; -> :jam
Sometimes you don’t want to refer to a string or keyword, but want to find a particular letter that someone typed on the keyboard. To do that, you would type the letter preceded by a backslash—for example, to find the letter “j” you would type \j, and Clojure would know you mean the character (or char, for short). This is great because if we just want one character, we don’t need to have the overhead of an entire string:
\j
;; -> \j
Let’s see. Any other basic simple values that evaluate to themselves? Oh yes, booleans. Let’s do true
and false
together:
true
;; -> true
false
;; -> false
There is one more. The small, but important nil
. It represents the absence of a value in Clojure. Hi, nil
:
nil
;; -> nil
We have just seen simple values in Clojure. We can now do something with them. One of the things that we can do with them is to use them in expressions. We have already done this with some simple divison. Here is another example with addition:
(
+
1
1
)
;; -> 2
The expression evaluated to a result that is a value of 2.
These expressions can also be nested:
(
+
1
(
+
8
3
))
;; -> 12
In this case, the inner expression of (+ 8 3)
was evaluated first and then 1 was added to the result.
We can use simple values in expressions. We can also use these values in other Clojure data collections. For example, if we want to have a collection of strings or integers, we need a way to put them all together. This is exactly what we are going to look at with Clojure’s collections.
Put Your Clojure Data in Collections
There are a number of ways to organize your data in Clojure collections. There are lists, vectors, maps, and sets. Which one you use depends on how you need to arrange and access your data. In the following sections, we will cover all the different collections, starting with simple lists.
Using a List Collection
Lists are collections of things. One of the things that makes lists special is that those things come in a given order. Let’s take a look at how to make a list in Clojure. We promised earlier to use some storytelling to make the code examples more fun. Now that we finally get to talk about something as exciting as lists, let’s take a moment to set the scene.
When Alice chased the white rabbit down the rabbit hole, she fell down and down. In fact, she fell so long that she had time to pick up and inspect jam jars on little shelves all along the hole. Our list will have some of the various things that she saw.
To create a list in Clojure, simply put a quote in front of the parens, and put your data inside of them:
'
(
1
2
"jam"
:marmalade-jar
)
;; -> (1 2 "jam" :marmalade-jar)
We can mix and match values such as strings, integers, and keywords in our collection. Clojure doesn’t mind. It also doesn’t mind if you use commas to separate items in your list. It will just ignore them and treat them as whitespace:
'
(
1
,2
,"jam"
,:bee
)
;; -> (1 2 "jam" :bee)
You will hear the word idiomatic quite often in relation to Clojure. This refers to the style in which Clojurists write their code. In Clojure collections, you can use commas, but it is idiomatic not to.
Tip
Commas are ignored in Clojure. Leave them behind, and use spaces to separate your elements in a collection.
What Can We Do with Lists?
How do we manipulate these lists? For example, how do we get the first element of the list?
Here we are going to need some functions to help us.
You can think of the list being made up of two parts: the first element of the list, and everything else. The first
function returns the first element of the list. The rest
function returns a list of all of the remaining elements:
(
first
'
(
:rabbit
:pocket-watch
:marmalade
:door
))
;; -> :rabbit
(
rest
'
(
:rabbit
:pocket-watch
:marmalade
:door
))
;; -> (:pocket-watch :marmalade :door)
We are of course free to nest the first
and rest
functions:
(
first
(
rest
'
(
:rabbit
:pocket-watch
:marmalade
:door
)))
;; -> :pocket-watch
(
first
(
rest
(
rest
'
(
:rabbit
:pocket-watch
:marmalade
:door
))))
;; -> :marmalade
(
first
(
rest
(
rest
(
rest
'
(
:rabbit
:pocket-watch
:marmalade
:door
)))))
;; -> :door
If we get a little crazy with the rest
s and get to the end of a list, we will find nil
waiting for us, which means of course, nothing. In our case, it also means the end of the list:
(
first
(
rest
(
rest
(
rest
(
rest
'
(
:rabbit
:pocket-watch
:marmalade
:door
))))))
;; -> nil
At first glance, this might seem like a very strange way to traverse through a list. However, the simplicity of this turns out to be very powerful in building up recursive functions, which will we delve into more in Chapter 2.
So far we have just been constructing our lists by having all the elements in them at one time. How do we go about building up a list? The is answer is simple, yet powerful. We can build up lists with just one function, called cons
.
The cons
function takes two arguments. The first is the element we want to add, and the second is the list that we want to add it to. So if we want to add something to an empty list, it would look like this:
(
cons
5
'
())
;; -> (5)
The end of list is specified with a nil
, so we could do the same thing by cons
ing an element with nil
:
;; building the list with a nil
(
cons
5
nil
)
;; -> (5)
From here, we can start building up more:
(
cons
4
(
cons
5
nil
))
;; -> (4 5)
And more:
(
cons
3
(
cons
4
(
cons
5
nil
)))
;; -> (3 4 5)
And even more:
(
cons
2
(
cons
3
(
cons
4
(
cons
5
nil
))))
;; -> (2 3 4 5)
Now we have built up our beautiful list, but it was a bit of work. It is good to know that we have the quote/parens shortcut available to us, as well as a list
function. Each of these are easy ways to make lists out of any number of things:
'
(
1
2
3
4
5
)
;; -> (1 2 3 4 5)
(
list
1
2
3
4
5
)
;; -> (1 2 3 4 5)
Lists are fine when you just want to get an element off the top of the list. But what if you have a collection of things and you want to get the element that is right in the middle? In other words, what if you need index access? This is when you need a vector.
Using Vectors for Collecting Data by Index
Vectors are very handy and quite common in Clojure. You can spot them by their square brackets:
[
:jar1
1
2
3
:jar2
]
;; -> [:jar1 1 2 3 :jar2]
The first
and rest
operators work on vectors:
(
first
[
:jar1
1
2
3
:jar2
])
;; -> :jar1
(
rest
[
:jar1
1
2
3
:jar2
])
;; -> (1 2 3 :jar2)
Unlike lists, in vectors, you have fast index access to the elements.
There is an nth
function that allows you to access the vector element at a given index:
(
nth
[
:jar1
1
2
3
:jar2
]
0
)
;; -> :jar1
(
nth
[
:jar1
1
2
3
:jar2
]
2
)
;; -> 2
Another useful function is last
, which returns the last element in the vector:
;; last with a vector
(
last
[
:rabbit
:pocket-watch
:marmalade
])
;; -> :marmalade
;; last with a list
(
last
'
(
:rabbit
:pocket-watch
:marmalade
))
;; -> :marmalade
Although you can use nth
and last
on lists as well as vectors, you will get better index access performance with vectors.
This is because a list needs to start at the beginning to find its way to the element it wants, as shown in Figure 1-1.
On the other hand, a vector can easily access the element, without having to traverse the whole structure to get to it, as illustrated in Figure 1-2.
If you need to access the elements of collection by index, use a vector.
Both vectors and lists are considered Clojure collections. There are also other collection types that we will cover a bit later. Before we do, let’s pause for a moment and talk about things that these collections have in common.
What Collections Have in Common
All collections are immutable and persistent. Immutable means that the value of the collection does not change. When we ask to cons
an element to a collection, the original collection is not changed. We are returned a new version of the structure with just the element added to it. Persistent means that these collections will do smart creations of new versions of themselves by using structural sharing.
As we just saw in the example, collections support the sequence functions: first
, rest
, and last
. The are a couple more collection functions we should mention as well. The count
function returns the size of the collection:
(
count
[
1
2
3
4
])
;; -> 4
The conj
function is rather interesting. It adds one or more elements to the collection. However, it adds them in the most natural way for that data structure. For vectors, it will add the elements at the end of the collection:
;; conj adds to the end of vectors
(
conj
[
:toast
:butter
]
:jam
)
;; -> [:toast :butter :jam]
;; multiple elements added on end of vectors
(
conj
[
:toast
:butter
]
:jam
:honey
)
;; -> [:toast :butter :jam :honey]
Figure 1-3 illustrates using conj
with a vector.
For lists, it will add them on to the beginning:
;; conj adds to the front of lists
(
conj
'
(
:toast
:butter
)
:jam
)
;; -> (:jam :toast :butter)
;; multiple elements added to the front of lists
(
conj
'
(
:toast
:butter
)
:jam
:honey
)
;; -> (:honey :jam :toast :butter)
Figure 1-4 illustrates using conj
with a list.
Note
conj
adds to a collection in the most natural way for the data structure. For lists, it adds to the beginning. For vectors, it adds to the end.
Ready for another type of collection? What if we want to have a collection of things that are key-value pairs? These are very useful indeed. They are called maps in Clojure.
Maps for Storing Key-Value Pairs of Data
Maps are extremely useful and used extensively in Clojure as a way of storing structured data by key-value pairs in an easily retrievable way. They are recognized by their curly braces:
{
:jam1
"strawberry"
:jam2
"blackberry"
}
;; -> {:jam2 "blackberry", :jam1 "strawberry"}
Commas, again, are considered whitespace in Clojure. Maps are the one place that it can be idiomatic to leave the commas in for readability. Leave them in if it helps you:
{
:jam1
"strawberry"
,:jam2
"blackberry"
}
;; -> {:jam2 "blackberry", :jam1 "strawberry"}
{
:jam1
"strawberry"
:jam2
"blackberry"
}
;; -> {:jam2 "blackberry", :jam1 "strawberry"}
You can get values out of maps using the get
function:
;; explicit get
(
get
{
:jam1
"strawberry"
:jam2
"blackberry"
}
:jam2
)
;; -> "blackberry"
There is also a way to provide a default if the key is not found. Simply put the default value as the last argument of the get
:
(
get
{
:jam1
"strawberry"
:jam2
"blackberry"
}
:jam3
"not found"
)
;; -> "not found"
A more idiomatic way to get the value from the map without using a get
, if you have a keyword as a key, is to use the key itself as a function. Keywords are by far the most common type of key used in the maps:
;; getting using the key as the function
(
:jam2
{
:jam1
"strawberry"
:jam2
"blackberry"
:jam3
"marmalade"
})
;; -> "blackberry"
Which one should you use? It depends on the situation of course, but generally speaking, using the key as the function is more idiomatic. However, sometimes using an explicit get
is clearer to read in the code.
The keys
and vals
functions return just the keys or values of the map:
;; the keys function
(
keys
{
:jam1
"strawberry"
:jam2
"blackberry"
:jam3
"marmalade"
})
;; -> (:jam3 :jam2 :jam1)
;;the vals function
(
vals
{
:jam1
"strawberry"
:jam2
"blackberry"
:jam3
"marmalade"
})
;; -> ("marmalade" "blackberry" "strawberry")
What if we need to “update” a map value? For example, if something happened to marmalade and we needed to replace it with another jam. Changing values is a pretty common thing to want to do in code. Remember, though, the collections are immutable, so when we are talking about “updating” a value, we are speaking shorthand for returning a new data structure with the updated value in it. Clojure’s persistent data structures use structural sharing that does the creation very efficiently.
Note
Remember, collections are immutable. A function to change a collection gives you back a new version of the collection.
The assoc
function associates the new key-value pairs to map:
(
assoc
{
:jam1
"red"
:jam2
"black"
}
:jam1
"orange"
)
;; -> {:jam2 "black", :jam1 "orange"}
Given a map and a key, the dissoc
function returns a new map with the key-value pair removed:
(
dissoc
{
:jam1
"strawberry"
:jam2
"blackberry"
}
:jam1
)
;; -> {:jam2 "blackberry"}
The merge
function is also quite handy to merge the key-value pairs from one map to the other:
(
merge
{
:jam1
"red"
:jam2
"black"
}
{
:jam1
"orange"
:jam3
"red"
}
{
:jam4
"blue"
})
;; -> {:jam4 "blue", :jam3 "red", :jam2 "black", :jam1 "orange"}
There is one more type of collection that we haven’t covered yet. This is a collection of unique values, known as sets.
Using Sets for Unique Collections of Data
Sets are very useful for when you have a collection of elements with no duplicates. You can recognize them by the surrounding #{}
:
#
{
:red
:blue
:white
:pink
}
;; -> #{:white :red :blue :pink}
;; No duplicates allowed in the set at creation
#
{
:red
:blue
:white
:pink
:pink
}
;; -> IllegalArgumentException Duplicate key: :pink
The fact that they are sets lets us do some handy set operations as well like union
, difference
, and intersection
. In order to use them, you will need to prefix these functions with clojure.set
.
We’ll see some set
operations in action next. Figure 1-5 illustrates the set operations in the example.
The union
function takes the combined items of all of the sets:
(
clojure.set/union
#
{
:r
:b
:w
}
#
{
:w
:p
:y
})
;; -> #{:y :r :w :b :p}
The difference
function is almost like subtraction. It takes the elements away from one of the sets:
(
clojure.set/difference
#
{
:r
:b
:w
}
#
{
:w
:p
:y
})
;; -> #{:r :b}
The intersection
function returns only the shared elements between sets:
(
clojure.set/intersection
#
{
:r
:b
:w
}
#
{
:w
:p
:y
})
;; -> #{:w}
You can convert another type of collection to a set using the set function. This is useful for using set operations on things like vectors. Maps can be converted to sets as well. The key-value pairs are turned into vectors:
(
set
[
:rabbit
:rabbit
:watch
:door
])
;; -> #{:door :watch :rabbit}
(
set
{
:a
1
:b
2
:c
3
})
;; -> #{[:c 3] [:b 2] [:a 1]}
To get an element from a set, you can use the get
function. Or if the element you are looking for is a keyword, you can access it with that, just like how a map can use the key itself as a function.
Using an example from Alice in Wonderland, she is looking for the rabbit in the set of all the things she can see:
(
get
#
{
:rabbit
:door
:watch
}
:rabbit
)
;; -> :rabbit
(
get
#
{
:rabbit
:door
:watch
}
:jar
)
;; -> nil
We can also access it directly using the keyword:
(
:rabbit
#
{
:rabbit
:door
:watch
})
;; -> :rabbit
The set itself can be used as a function to do the same thing:
(
#
{
:rabbit
:door
:watch
}
:rabbit
)
;; -> :rabbit
There is also a way to query the set to see if an element is there with contains?
:
(
contains?
#
{
:rabbit
:door
:watch
}
:rabbit
)
;; -> true
(
contains?
#
{
:rabbit
:door
:watch
}
:jam
)
;; -> false
To add elements onto a set, the collection functions of conj
work just fine:
(
conj
#
{
:rabbit
:door
}
:jam
)
;; -> #{:door :rabbit :jam}
The disj
function is used to remove elements from sets:
(
disj
#
{
:rabbit
:door
}
:door
)
;; -> #{:rabbit}
Wow! Give yourself a pat on the back, we have just covered the basic structure of Clojure along with its collection data structures. Let’s take a moment for a quick review.
Summary of Simple Value and Collections
You now know about simple values in Clojure:
-
Strings
-
Integers
-
Ratios
-
Decimals
-
Keywords
-
Characters
-
Booleans
You know that these simple values can be used in functions and expressions. Expressions in Clojure include the operator or function first, followed by the parameters.
You can also use these values in collections:
-
Lists are for collections of data that you want to access from the top of the list.
-
Vectors are for collections of data that you want to access anywhere by position.
-
Maps are for key-value pairs, which is great for organizing data and having easy access.
-
Sets are for collections of unique elements; by grouping elements together in a set, you can perform set operations on them.
You also know how to use functions on these collections to build them up, get data out of them, and modify them (in an immutable way, of course).
You know about lists in Clojure, but there is something special about them. They are actually at the heart of Clojure. We will talk about why they are special next.
Lists Are the Heart of Clojure
This basic structure of Clojure comes from its LISP nature. The actual name LISP comes from LISt Processing. Lists are a key data structure.
Let’s revisit a list:
'
(
"marmalade-jar"
"empty-jar"
"pickle-jam-jar"
)
What makes this a list? The prefix quote mark on the front. Why do we need it? This is because in LISP, the first element in an expression is considered to be an operator or function. Any elements after the first are considered to be data for the operator.
This is what would happen if we didn’t have it:
(
"marmalade-jar"
"empty-jar"
"pickle-jam-jar"
)
;; -> ClassCastException String cannot be cast to IFn
The error message is trying to tell us what we suspected—it tried to call the string as a function because it was the first element. Alas, the string—even though it is a very nice marmalade jar string—is not a function.
What would happen if we put a quote in front of the addition expression we did earlier?
'
(
+
1
1
)
;; -> (+ 1 1)
When the REPL evaluated it, it returned the expression rather than the number 2. Clojure considers it to now be a list of three elements:
-
The first element is the operator
-
The second element is the integer 1
-
The third element is the integer 1
We can treat this as just a list and do things like get the first element from it:
(
first
'
(
+
1
1
))
;; -> +
All of this leads us to a very important discovery: Code is data!
All Clojure code is made of lists of data.
This simplicity is the heart of Clojure. It not only makes it elegant, but powerful.
We are ready for the next step. So far, we have been typing in the same code over and over again. It has been a bit repetitive. In the real world, Clojurists use symbols to represent data somewhat like other languages use variables. We are going to make our lives much easier by learning how to use them.
Symbols and the Art of Binding
Clojure symbols refer to values. When a symbol is evaluated, it returns the thing it refers to. So instead of typing in a specific vector of:
[
1
2
3
4
]
over and over again, we can assign it any symbol we like, like foo
. Then when we evaluate foo
, it will return the value of:
[
1
2
3
4
]
It is easier to see examples of symbols in action, so let’s take a look at creating them with def
.
def
allows us to give something a name, so we can refer to it from anywhere in our code. It doesn’t bind a symbol with the thing directly, it does it through a var (the reason is not important right now). Vars are not the same as variables from most other programming languages because we don’t expect their values to change during the course of the program. In Clojure, there are powerful tools to handle the stuff that changes with time that we will discuss more in Chapter 3.
Let’s give the string "Alice"
a name:
(
def
developer
"Alice"
)
;; -> #'user/developer
What did def
do? It created a var object in the default namespace
of our REPL called user
(we will talk more about namespaces shortly), for the symbol developer
. Now when we evaluate developer
in the REPL, it will evaluate to "Alice"
. We could also reference the symbol by its namespace, which is prefixed by a slash (/). We don’t have to do that in this case, because the REPL starts in the user namespace:
developer
;; -> "Alice"
user/developer
;; -> "Alice"
Note
Vars with namespaces might seem to be quite different from the programming language you are used to. Just like you have a full name that consists of a first and a last name, which helps differentiate yourself from other people with the same first name, so vars use namespaces. The fully qualified name of the var includes the namespace followed by a forward slash, then the name of the var:
user/developer
In the line user/developer
, user
is the namespace and developer
is the name of the var. We can use the shorthand version of just developer
if we are in the same namespace of the var. But we can also always use the fully qualified name of the var that includes the namespace.
This is quite an exciting discovery. We now have the ability to create shorthand symbols for things and refer to them later. We no longer need to type them out all the time.
But there are a couple of problems. First, we don’t really want to create a global var for all the things. Second, what do we do if we want to have a temporary var? An example of this is if we want to set the developer’s name to a var for a specific calcuation, but we don’t want to interfere with the original value.
Using let
allows us to have bindings to symbols that are only available within the context of the let
. This is incredibly useful and a fundamental building block of the language. Let’s looks at what happens if we change the binding of the symbol developer
to “Alice in Wonderland” in a let
:
(
def
developer
"Alice"
)
;; -> #'user/developer
(
let
[
developer
"Alice in Wonderland"
]
developer
)
;; -> "Alice in Wonderland"
developer
;; -> "Alice"
The bindings of let
are in a vector form. It expects pairs of symbol and values. This is yet another example of the power of code as data. In fact, most everything we are learning is built on the simple structure of Clojure.
Remember, what happens in a let
, stays in the let
. So if you bind a symbol inside the let
that doesn’t exist outside, and then try to reference it later, it will give you an error:
(
let
[
developer
"Alice in Wonderland"
rabbit
"White Rabbit"
]
[
developer
rabbit
])
;; -> ["Alice in Wonderland" "White Rabbit"]
rabbit
;; -> CompilerException java.lang.RuntimeException:
;; -> Unable to resolve symbol: rabbit in this context
Note
Remember, what happens in a let
, stays in the let
.
Great! With symbols and binding we can now:
We made our own data, now let’s make our own functions. And because code is data, the process is very similar.
Creating Our Own Functions
Creating functions is one of the most common and important building blocks of Clojure programs. We can create functions and assign symbols to them and call them later. Clojure is a functional language, so functions are one of the main features. So far, we have just been using built-in functions to the language. But we can make our own!
Let’s first look to see how to make our function with defn
.
defn
is very similar to def
, but it creates vars for functions. It takes the following as arguments: the name of the function, a vector of parameters, and finally the body of the function.
To call the function, simply use the function with parens. When we call a function, Clojure will evaluate it and return a result.
A function can be defined with no parameters by using an empty vector. This one takes no params and returns the string “Off we go!”:
(
defn
follow-the-rabbit
[]
"Off we go!"
)
;; -> #'user/follow-the-rabbit
(
follow-the-rabbit
)
;; -> "Off we go!"
A function can also be defined with parameters. This function takes two jams and evaluates to a map containing those jams:
(
defn
shop-for-jams
[
jam1
jam2
]
{
:name
"jam-basket"
:jam1
jam1
:jam2
jam2
})
;; -> #'user/shop-for-jams
(
shop-for-jams
"strawberry"
"marmalade"
)
;; -> {:name "jam-basket", :jam1 "strawberry", :jam2 "marmalade"}
Sometimes you need to use a function briefly, but don’t want to name it. These are anonymous functions. An anonymous function in Clojure can be expressed with the fn
operator. It takes a vector of parameters and then the body of the function. It can be called, again, simply by calling the function with surrounding parens:
;;returns back a function
(
fn
[]
(
str
"Off we go"
"!"
))
;; -> #<user$eval790$fn__791 user$eval790$fn__791@2ecd16a2>
;;invoke with parens
((
fn
[]
(
str
"Off we go"
"!"
)))
;; -> "Off we go!"
In fact, defn
is just the same as using def
and binding the name to the anonymous function:
(
def
follow-again
(
fn
[]
(
str
"Off we go"
"!"
)))
;; -> #'user/follow-again
(
follow-again
)
;; -> "Off we go!"
There is a shorthand form of an anonymous function, too. It uses a #
in front of the parens:
(
#
(
str
"Off we go"
"!"
))
;; -> "Off we go!"
If there is one parameter, you can use the percent sign (%
) to represent it:
(
#
(
str
"Off we go"
"!"
" - "
%
)
"again"
)
;; -> "Off we go! - again"
Or if there are multiple parameters, you can number the percent signs—for example, %1, %2, and so on:
(
#
(
str
"Off we go"
"!"
" - "
%1
%2
)
"again"
"?"
)
;; -> "Off we go! - again?"
You can now create all sorts of symbols. How do we keep everything organized? With object-oriented languages, we rely on objects to contain and group similar functions. Clojure does this in another way. It uses namespaces.
Keep Your Symbols Organized in Namespaces
Namespaces are organized and controlled access to vars. We saw that when we created a var with def
or defn
, it created it in the default namespace for the REPL, which is called “user.” You can create your own namespace and switch to it using ns
. Any vars created will now be created with this namespace.
Let’s create a new namespace now. This one is going to be for Alice’s favorite foods. We are going to call it alice.favfoods
:
(
ns
alice.favfoods
)
;; -> nil
At this point, the current namespace of the REPL has been switched from our default one that starts up the REPL to our newly defined one, alice.favfoods
. We can check in Clojure by seeing the value that *ns*
returns. The asterisks on either side of the ns
are called earmuffs and are used as a convention for things that are intended for rebinding (to change):
*ns*
;; -> #<Namespace alice.favfoods>
If we define something here, the var will be directly accessible:
(
def
fav-food
"strawberry jam"
)
;; -> #'alice.favfoods/fav-food
fav-food
;; -> "strawberry jam"
It also can be accessed via the fully qualified namespace alice.favfoods/fav-food
:
alice.favfoods/fav-food
;; -> "strawberry jam"
If we switch to to another namespace, the symbol will no longer be resolved:
(
ns
rabbit.favfoods
)
;; -> nil
fav-food
;; -> CompilerException java.lang.RuntimeException:
;; -> Unable to resolve symbol: fav-food in this context
We could define a var with the symbol, with a different namespace, to another value:
(
ns
rabbit.favfoods
)
(
def
fav-food
"lettuce soup"
)
;; -> #'rabbit.favfoods/fav-food
fav-food
;; -> "lettuce soup"
To refer to Alice’s favorite food, we would need to evaluate the fully qualified namespace:
alice.favfoods/fav-food
;; -> "strawberry jam"
Clojure libs are made up of these names and symbols associated with these namespaces. There are three main ways of using libs in your namespace using require
. The first way is to use the require
expression with the namespace as the argument. This will simply load the lib and enable access to it via the fully qualified namespace. When we used the set functions earlier, we had to use the fully qualified namespace. Remember clojure.set/union
?
;; Union
(
clojure.set/union
#
{
:r
:b
:w
}
#
{
:w
:p
:y
})
;; -> #{:y :r :w :b :p}
The clojure.set
namespace gets auto-required into our user
namespace when the REPL starts up. If it didn’t get loaded for us, we could do it using require
:
(
require
'clojure.set
)
The second way is to use the require
expression with an alias using :as
. This allows access to the symbols using the alias as a prefix to the name, rather than the original namespace:
(
ns
wonderland
)
;; -> nil
;; using an alias
(
require
'
[
alice.favfoods
:as
af
])
;; -> nil
af/fav-food
;; -> "strawberry jam"
Although you can use require
on its own, it is common to see it nested within the ns
, with a keyword and vector:
(
ns
wonderland
(
:require
[
alice.favfoods
:as
af
]))
af/fav-food
;; -> "strawberry jam"
The last way is to use require
with the namespace and the :refer :all
options. This loads all the symbols and makes them directly accessible in the current namespace, just by the symbol name. This can be a bit risky, because naming conflicts can occur. It is also harder to read the code and figure out which lib a function is coming from. What happens if we try to refer to the symbol fav-food
from two different namespaces?
(
ns
wonderland
(
:require
[
alice.favfoods
:refer
:all
]
[
rabbit.favfoods
:refer
:all
]))
;; -> Exception:
;; fav-food already refers to: #'alice.favfoods/fav-food
;; in namespace: wonderland
Most Clojure code will use libs with a require
and specify an alias using :as
. Exceptions are tests, where it is common to use the clojure.test
functions directly as well as the namespace you are testing. There is also a use
expression that is the same as the require
with :refer :all
. Although the use
is still acceptable, the require
with :refer :all
is preferred.
Let’s take a moment to look at all the things you can now do!
-
You can create and manipulate collections of data.
-
You can create functions.
-
You can create symbols.
-
You can organize your code into namespaces.
Here is a nice example for practice. It is a function that takes in the rabbit’s favorite foods and Alice’s favorite foods and returns things that they both like to eat. It also uses our namespaces and symbols:
(
ns
wonderland
(
:require
[
clojure.set
:as
s
]
)
)
(
defn
common-fav-foods
[
foods1
foods2
]
(
let
[
food-set1
(
set
foods1
)
food-set2
(
set
foods2
)
common-foods
(
s/intersection
food-set1
food-set2
)
]
(
str
"Common Foods: "
common-foods
)
)
)
(
common-fav-foods
[
:jam
:brownies
:toast
]
[
:lettuce
:carrots
:jam
]
)
;; -> "Common Foods: #{:jam}"
We require the
clojure.set
namespace and we are going to use the shorthand letters
to refer to it.The set of
foods1
is bound to a symbolfood-set1
The set of
foods2
is bound to a symbolfood-set2
We use the intersection function from the
clojure.set
namespace and use the symbolsfood-set1
andfood-set2
.
You are on your way in your journey to Clojure understanding. You have the ability to structure your Clojure code
into simple and beautiful expressions. You know how to fill these expressions with simple values like integers and strings,
or Clojure collections, like maps and vectors. You can create global symbolic bindings with def
and defn
, and local bindings in a let
.
You can even organize these bindings into namespaces. Finally, you know of the true nature of Clojure’s code as data. With all these fundamentals under your belt, you are ready to move on to the next chapter where
we will take this structure of code and make it move and flow with functional transformations.
Get Living Clojure now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.