actual elements, this difference in asymptotic complexity will shrink. But it’s an
extremely efficient way to determine the smallest n values in a large unsorted (Knuth
1998) sequence.
6.5
Summary
We’ve covered the topics of immutability, persistence, and laziness in this chapter. Clo-
jure’s core composite data types are all immutable and persistent by default, and
though this fact might presuppose fundamental inefficiencies, we’ve shown how Clo-
jure addresses them. The implementation of a persistent sorted binary tree demon-
strated how structural sharing eliminated the need for full copy-on-write. But
structural sharing isn’t enough to guarantee memory efficiency, and that’s where the
benefits of laziness come into the fold. The implementation of a lazy, tail-recursive
quicksort demonstrated that laziness guarantees that sequences won’t be fully realized
in memory at any given step.
In the next chapter, we’ll dive into Clojure’s notion of functional programming.
Along the way, you’ll notice that much of the shape of functional implementations in
Clojure will be influenced by the topics discussed in this chapter.
Download from Wow! eBook <www.wowebook.com>
Functional
programming
This chapter covers
Functions in all their forms
Closures
Thinking recursively
Putting it all together: A* pathfinding
At the core of functional programming is a formal system of computation known as
the lambda calculus (Pierce 2002). Clojure functions, in adherence with the lambda
calculus, are first-class—they can be both passed as arguments and returned as
results from other functions. This book isn’t about the lambda calculus. Instead
we’ll explore Clojure’s particular flavor of functional programming. We’ll cover a
vast array of useful topics, including function composition, partial evaluation,
recursion, lexical closures, pure functions, function constraints, higher-order func-
tions, and first-class functions. We’ll use that last item as our starting point.
125
Download from Wow! eBook <www.wowebook.com>
126
CHAPTER 7 Functional programming
7.1
Functions in all their forms
In chapter 5, we mentioned that most of Clojure’s composite types can be used as
functions of their elements. As a refresher, recall that vectors are functions of their
indices, so executing ([:a :b] 0) will return :a. But this can be used to greater effect
by passing the vector as a function argument:
(map [:chthon :phthor :beowulf :grendel] #{0 3})
;=> (:chthon :grendel)
In the example, we’ve used the vector as the function to map over a set of indices,
indicating its first and fourth elements by index. Clojure collections offer an interest-
ing juxtaposition, in that not only can Clojure collections act as functions, but Clojure
functions can also act as data—an idea known as first-class functions.
7.1.1
First-class functions
In a programming language such as Java, there’s no notion of a standalone function.1
Instead, every problem solvable by Java must be performed with the fundamental phi-
losophy that everything is an object. This view on writing programs is therefore rooted
in the idea that behaviors within a program must be either modeled as class instances
or attached to them—wise or not. Clojure, on the other hand, is a functional pro-
gramming language and views the problem of software development as the applica-
tion of functions to data. Likewise, functions in Clojure enjoy equal standing with
data—functions are first-class citizens. Before we start, we should define what makes
something first-class:
It can be created on demand.
It can be stored in a data structure.
It can be passed as an argument to a function.
It can be returned as the value of a function.
Those of you coming from a background in Java might find the idea of creating func-
tions on demand analogous to the practice of creating anonymous inner classes to
handle Swing events (to name only one use case). Though similar enough to start on
the way toward understanding functional programming, it’s not a concept likely to
bear fruit, so don’t draw conclusions from this analogy.
CREATING FUNCTIONS ON DEMAND USING COMPOSITION
Even a cursory glance at Clojure is enough to confirm that its primary unit of compu-
tation is the function, be it created or composed of other functions:
(def fifth (comp first rest rest rest rest))
(fifth [1 2 3 4 5])
;=> 5
1
Although the likely inclusion of closures in some future version of Java should go a long way toward invalidat-
ing this. Additionally, for those of you coming from a language such as Python, Scala, or another Lisp, the
notion of a first-class function is likely not as foreign as we make it out to be.
Download from Wow! eBook <www.wowebook.com>
Functions in all their forms
127
The function fifth wasn’t defined with fn or defn forms shown before, but instead
built from existing parts using the comp (compose) function. But it may be more inter-
esting to take the idea one step further by instead proving a way to build arbitrary nth
functions2 as shown here:
(defn fnth [n]
(apply comp
(cons first
(take (dec n) (repeat rest)))))
((fnth 5) '[a b c d e])
;=> e
The function fnth builds a list of the function rest of the appropriate length with a
final first consed onto the front. This list is then fed into the comp function via
apply, which takes a function and a sequence of things and effectively calls said func-
tion with the list elements as its arguments. At this point, there’s no longer any doubt
that the function fnth builds new functions on the fly based on its arguments. Creat-
ing new functions in this way is a powerful technique, but it takes some practice to
think in a compositional way. It’s relatively rare to see more than one open-parenthe-
sis in a row like this in Clojure, but when you see it, it’s almost always because a func-
tion (such as fnth) is creating and returning a function that’s called immediately. A
general rule of thumb is that if you need a function that applies a number of functions
serially to the return of the former, then composition is a good fit:
(map (comp keyword #(.toLowerCase %) name) '(a B C))
;=> (:a :b :c)
Splitting functions into smaller, well-defined pieces fosters composability and, as a
result, reuse.
CREATING FUNCTIONS ON DEMAND USING PARTIAL FUNCTIONS
There may be times when instead of building a new function from chains of other
functions as comp allows, you need to build a function from the partial application of
another:
((partial + 5) 100 200)
;=> 305
The function partial builds (Tarver 2008) a new function that consists of the partial
application of the single argument 5 to the addition function. When the returned par-
tial function is passed the arguments 100 and 200, the result is their summation plus
that of the value 5 captured by partial.
PARTIAL APPLICATION ISN’T CURRYING The use of partial differs from the
notion of currying in a fundamental way. A function built with partial will
attempt to evaluate whenever it’s given another argument. A curried function
2 We know that Clojure provides an nth function that works slightly differently, but in this case please indulge
our obtuseness.
Download from Wow! eBook <www.wowebook.com>
128
CHAPTER 7 Functional programming
on the other hand will return another curried function until it receives a pre-
determined number of arguments—only then will it evaluate. Because Clojure
allows functions of variable number of arguments, currying makes little sense.
We’ll discuss more about utilizing partial later in this section, but as a final point
observe that ((partial + 5) 100 200) is equivalent to (#(apply + 5 %&) 100 200).
REVERSING TRUTH WITH COMPLEMENT
One final function builder discussed here is the complement function. Simply put, this
function takes a function that returns a truthy value and returns the opposite truthy
value:
(let [truthiness (fn [v] v)]
[((complement truthiness) true)
((complement truthiness) 42)
((complement truthiness) false)
((complement truthiness) nil)])
;=> [false false true true]
((complement even?) 2)
;=> false
Note that (complement even?) is equivalent to (comp not even?).
USING FUNCTIONS AS DATA
First-class functions can not only be treated as data; they are data. Because a function is
first-class, it can be stored in a container expecting a piece of data, be it a local, a refer-
ence, collections, or anything able to store a java.lang.Object. This is a significant
departure from Java, where methods are part of a class but don’t stand alone at run-
time (Forman 2004). One particularly useful method for treating functions as data is
the way that Clojure’s testing framework clojure.test stores and validates unit tests
in the metadata of a Var holding a function. These unit tests are keyed with the :test
keyword, laid out as
(defn join
{:test (fn []
(assert
(= (join "," [1 2 3]) "1,3,3")))}
[sep s]
(apply str (interpose sep s)))
We’ve modified our old friend join by attaching some metadata containing a faulty
unit test. Of course, by that we mean that the attached unit test is meant to fail in this
case. The clojure.test/run-tests function is useful for running attached unit tests
in the current namespace:
(use '[clojure.test :as t])
(t/run-tests)
; Testing user
;
; ERROR in (join) (test.clj:646)
; ...
Download from Wow! eBook <www.wowebook.com>
Functions in all their forms
129
; actual: java.lang.AssertionError:
; Assert failed: (= (join "," [1 2 3]) "1,3,3")
; ...
As expected, the faulty unit test for join failed. Unit tests in Clojure only scratch the
surface of the boundless spectrum of examples using functions as data, but for now
they’ll do, as we move into the notion of higher-order functions.
7.1.2
Higher-order functions
A higher-order function is a function that does at least one of the following:
Takes one or more functions as arguments
Returns a function as a result
A Java programmer might be familiar with the practices of subscriber patterns or
schemes using more general-purpose callback objects. There are scenarios such as
these where Java treats objects like functions, but as with anything in Java, you’re really
dealing with objects containing privileged methods.
FUNCTIONS AS ARGUMENTS
In this book, we’ve used and advocated the use of the sequence functions map, reduce,
and filter—all of which expect a function argument that’s applied to the elements
of the sequence arguments. The use of functions in this way is ubiquitous in Clojure
and can make for truly elegant solutions. Let’s look at a simple example of a function
that takes a sequence of maps and a function working on each, and returns a
sequence sorted by the results of the function. The implementation in Clojure is
straightforward and clean:
(def plays [{:band "Burial", :plays 979, :loved 9}
{:band "Eno", :plays 2333, :loved 15}
{:band "Bill Evans", :plays 979, :loved 9}
{:band "Magma", :plays 2665, :loved 31}])
(def sort-by-loved-ratio (partial sort-by #(/ (:plays %) (:loved %))))
The function with the overly descriptive name sort-by-loved-ratio is built from the
partial application of the function sort-by and an anonymous function dividing the
:plays field by the :loved field. This is a simple solution to the problem presented,
and its usage is equally so:
(sort-by-loved-ratio plays)
;=> ({:band "Magma", :plays 2665, :loved 31}
{:band "Burial", :plays 979, :loved 9}
{:band "Bill Evans", :plays 979, :loved 9}
{:band "Eno", :plays 2333, :loved 15})
We intentionally used the additional higher-order function sort-by to avoid reimple-
menting core functions and instead build our program from existing parts. You should
strive for the same whenever possible.
Download from Wow! eBook <www.wowebook.com>
130
CHAPTER 7 Functional programming
FUNCTIONS AS RETURN VALUES
We’ve already used functions returning functions in this chapter with comp, partial,
and complement, but you could build functions that do the same. We’ll extend the ear-
lier example to provide a function that sorts rows based on some number of column
values. This is similar to the way that spreadsheets operate, in that you can sort on a
primary column while falling back on a secondary column to provide the sort order
on matching results in the primary. This behavior is typically performed along any
number of columns, cascading down from the primary column to the last; each sub-
group is sorted appropriately, as the expected result illustrates:
(sort-by (columns [:plays :loved :band]) plays)
;=> ({:band "Bill Evans", :plays 979, :loved 9}
{:band "Burial", :plays 979, :loved 9}
{:band "Eno", :plays 2333, :loved 15}
{:band "Magma", :plays 2665, :loved 31})
This kind of behavior sounds complex on the surface but is shockingly simple3 in its
Clojure implementation:
(defn columns [column-names]
(fn [row]
(vec (map row column-names))))
Running the preceding expression shows that the rows for Burial and Bill Evans
have a tertiary column sorting. The function columns returns another function
expecting a map. This return function is then supplied to sort-by to provide the
value on which the plays vector would be sorted. Perhaps you see a familiar pattern:
we apply the column-names vector as a function across a set of indices, building a
sequence of its elements at those indices. This action will return a sequence of the val-
ues of that row for the supplied column names, which is then turned into a vector so
that it can then be used as the sorting function,4 as structured here:
(vec (map (plays 0) [:plays :loved :band]))
;=> [979 9 "Burial"]
This resulting vector is then used by sort-by to provide the final ordering.
Building your programs using first-class functions in concert with higher-order
functions will reduce complexities and make your codebase more robust and exten-
sible. In the next subsection, we’ll explore pure functions, which all prior functions
in this section have been, and explain why your own applications should strive
toward purity.
3
Strictly speaking, the implementation of columns should use #(% row) instead of just row, because we can’t
always assume that the row is implemented as a map (a record might be used instead) and therefore directly
usable as a function. Records will be discussed further in chapter 8.
4
Because sort-by is higher-order, it naturally expects a function argument. As mentioned, vectors can also
be used as functions. However, as we will discuss in detail in section 10.4, all closure functions implement the
java.util.Comparator interface, which in this case is the driving force behind the sorting logic behind
sort-by!
Download from Wow! eBook <www.wowebook.com>
Functions in all their forms
131
Prefer higher-order functions when processing sequences
We mentioned in section 6.3 that one way to ensure that lazy sequences are never
fully realized in memory is to prefer (Hutton 1999) higher-order functions for process-
ing. Most collection processing can be performed with some combination of the fol-
lowing functions:
map, reduce, filter, for, some, repeatedly, sort-by, keep
take-while, and drop-while
But higher-order functions aren’t a panacea for every solution. Therefore, we’ll cover
the topic of recursive solutions deeper in section 7.3 for those cases when higher-
order functions fail or are less than clear.
7.1.3
Pure functions
Simply put, pure functions are regular functions that, through convention, conform to
the following simple guidelines:
The function always returns the same result, given some expected arguments.
The function doesn’t cause any observable side-effects.
Though Clojure is designed to minimize and isolate side-effects, it’s by no means a
purely functional language. But there are a number of reasons why you’d want to
build as much of your system as possible from pure functions, and we’ll enumerate a
few presently.
REFERENTIAL TRANSPARENCY
If a function of some arguments always results in the same value and changes no other
values within the greater system, then it’s essentially a constant, or referentially trans-
parent (the reference to the function is transparent to time). Take a look at pure func-
tion keys-apply:
(defn keys-apply [f ks m]
"Takes a function, a set of keys, and a map and applies the function
to the map on the given keys. A new map of the results of the function
applied to the keyed entries is returned."
(let [only (select-keys m ks)]
(zipmap (keys only) (map f (vals only)))))
(keys-apply #(.toUpperCase %) #{:band} (plays 0))
;=> {:band "BURIAL"}
Using another pure function manip-map, we can then manipulate a set of keys based
on a given function:
(defn manip-map [f ks m]
"Takes a function, a set of keys, and a map and applies
the function to the map on the given keys. A modified
version of the original map is returned with the results
of the function applied to each keyed entry."
(conj m (keys-apply f ks m)))
Download from Wow! eBook <www.wowebook.com>
132
CHAPTER 7 Functional programming
(manip-map #(int (/ % 2)) #{:plays :loved} (plays 0))
;=> {:band "Burial", :plays 489, :loved 4}
The functions keys-apply and manip-map are both5 pure functions, illustrated by the
fact that you can replace them in the context of a larger program with their expected
return values and not change the outcome. Pure functions exist outside the bounds of
time. But if you make either keys-apply or manip-map reliant on anything but its
arguments or generate a side-effect within, then referential transparency dissolves.
We’ll add one more function to illustrate this:
(defn halve! [ks]
(map (partial manip-map #(int (/ % 2)) ks) plays))
(halve! [:plays])
;=> ({:band "Burial", :plays 489, :loved 9}
{:band "Eno", :plays 1166, :loved 15}
{:band "Bill Evans", :plays 489, :loved 9}
{:band "Magma", :plays 1332, :loved 31})
The function halve! works against the global plays and is no longer limited to gener-
ating results solely from its arguments. Because plays could change at any moment,
there’s no guarantee that halve! would return the same value given any particular
argument.
OPTIMIZATION
If a function is referentially transparent, then it can more easily be optimized using
techniques such as memoization (discussed in chapter 12) and algebraic manipula-
tions (Wadler 1989).
TESTABILITY
If a function is referentially transparent, then it’s easier to reason about and therefore
more straightforward to test. Building halve! as an impure function forces the need
to test against the possibility that plays could change at any time, complicating mat-
ters substantially. Imagine the confusion should you add further impure functions
based on further external transient values.
7.1.4
Named arguments
Some programming languages allow functions to take named arguments; Python is
one such language, as seen here:
def slope(p1=(0,0), p2=(1,1)):
return (float(p2[1] - p1[1])) / (p2[0] - p1[0])
slope((4,15), (3,21))
#=> -6.0
slope(p2=(2,1))
#=> 0.5
slope()
#=> 1.0
5 These functions are based on a similar implementation created by Steven Gilardi.
Download from Wow! eBook <www.wowebook.com>
Functions in all their forms
133
The Python function slope calculates the slope of a line given two tuples defining
points on a line. The tuples p1 and p2 are defined as named parameters, allowing
either or both to be omitted in favor of default values, or passed in any order as a
named parameter. Clojure provides a similar feature using its destructuring mecha-
nism coupled with the optional arguments flag &. The same function would be written
using Clojure’s named arguments as in the following listing.
Listing 7.1 Named arguments in Clojure functions
(defn slope
[& {:keys [p1 p2] :or {p1 [0 0] p2 [1 1]}}]
(float (/ (- (p2 1) (p1 1))
(- (p2 0) (p1 0)))))
(slope :p1 [4 15] :p2 [3 21])
;=> -6.0
(slope :p2 [2 1])
;=> 0.5
(slope)
;=> 1.0
Clojure’s named arguments are built on the destructuring mechanism outlined in sec-
tion 3.3, allowing much richer ways to declare them.
7.1.5
Constraining functions with pre- and postconditions
Every function in Clojure can potentially be constrained on its inputs, its output, and
some arbitrary relationship between them. These constraints take the form of pre-
and postcondition vectors contained in a map defined in the function body. We can
simplify the slope function to the base case to more clearly illustrate the matter of
constraints:
(defn slope [p1 p2]
{:pre [(not= p1 p2) (vector? p1) (vector? p2)]
:post [(float? %)]}
(/ (- (p2 1) (p1 1))
(- (p2 0) (p1 0))))
The constraint map defines two entries: :pre constraining the input parameters and
:post the return value. The function calls in the constraint vectors are all expected to
return true for the constraints to pass (via logical and). In the case of the revised
slope function, the input constraints are that the points must not be equal, and they
must both be vectors. In the postcondition, the constraint is that the return result
must be a floating-point value. We run through a few scenarios in the following listing
to see how the new implementation works.
Download from Wow! eBook <www.wowebook.com>
134
CHAPTER 7 Functional programming
Listing 7.2 Testing the slope function constraints
(slope [10 10] [10 10])
Same points
; java.lang.AssertionError: Assert failed: (not= p1 p2)
(slope [10 1] '(1 20))
p2 is List
; java.lang.AssertionError: Assert failed: (vector? p2)
(slope [10 1] [1 20])
Float not returned
; java.lang.AssertionError: Assert failed: (float? %)
(slope [10.0 1] [1 20])
Any/all as floating point
;=> -2.111111111111111
Clojure also provides a simple assertion macro that can be used to emulate some pre-
and postconditions. Using assert instead of :pre is typically fairly straightforward.
But using assert instead of :post is cumbersome and awkward. On the contrary,
restricting yourself to constraint maps will cover most of the expected cases covered by
assert, which can be used to fill in the remaining holes (such as loop invariants). In
any case, constraint maps provide standard hooks into the assertion machinery of Clo-
jure, while using assert is by its nature ad hoc. Yet another advantage for :pre and
:post is that they allow the assertions to come from a different source than the body
of the function, which we’ll address next.
DECOUPLING ASSERTIONS FROM FUNCTIONS
The implementation of slope corresponds to a well-established mathematic property.
As a result, it makes perfect sense to tightly couple the constraints and the work to be
done to perform the calculation. But not all functions are as well-defined as slope,
and therefore could benefit from some flexibility in their constraints. Imagine a func-
tion that takes a map, puts some keys into it, and returns the new map, defined as
(defn put-things [m]
(into m {:meat "beef" :veggie "broccoli"}))
(put-things {})
;=> {:meat "beef", :veggie "broccoli"}
How would you add constraints to put-things? You could add them directly to the
function definition, but the consumers of the map might have differing requirements
for the entries added. Instead, observe how we can abstract the constraints into
another function:
(defn vegan-constraints [f m]
{:pre [(:veggie m)]
:post [(:veggie %) (nil? (:meat %))]}
(f m))
(vegan-constraints put-things {:veggie "carrot"})
; java.lang.AssertionError: Assert failed: (nil? (:meat %))
The vegan-constraints function applies specific constraints to an incoming function,
stating that the map coming in and going out should have some kind of veggie and
Download from Wow! eBook <www.wowebook.com>
Closures
135
should never have meat in the result. The beauty of this scheme is that you can create
contextual constraints based on the appropriate expected results, as shown next.
Listing 7.3 Menu constraints
(defn balanced-diet [f m]
{:post [(:meat %) (:veggie %)]}
Make sure there’s
(f m))
meat and veggie
(balanced-diet put-things {})
;=> {:veggie "broccoli", :meat "beef"}
(defn finicky [f m]
Never change
{:post [(= (:meat %) (:meat m))]}
the meat
(f m))
(finicky put-things {:meat "chicken"})
; java.lang.AssertionError: Assert failed: (= (:meat %) (:meat m))
By pulling out the assertions into a wrapper function, we’ve detached some domain-
specific requirements from a potentially globally useful function and isolated them in
aspects (Laddad 2003). By detaching pre- and postconditions from the functions them-
selves, you can mix in any implementation that you please, knowing that as long as it
fulfills the contract (Meyer 1991), its interposition is transparent. This is only the
beginning of the power of Clojure’s pre- and postconditions, and we’ll come back to it
a few times more to see how it can be extended and utilized.
Now that we’ve covered some of the powerful features available via Clojure’s func-
tions, we’ll take a step further by exploring lexical closures.
7.2
Closures
On his next walk with Qc Na, Anton attempted to impress his master by saying
“Master, I have diligently studied the matter, and now understand that objects are
truly a poor man’s closures.” Qc Na responded by hitting Anton with his stick,
saying “When will you learn? Closures are a poor man’s object.” At that moment,
Anton became enlightened.
—Part of a parable by Anton van Straaten
It took only 30 years, but closures (Sussman 1975) are now a key feature of main-
stream programming languages—Perl and Ruby support them, and JavaScript derives
much of what power it has from closures. So what’s a closure? In a sentence, a closure is
a function that has access to locals from a larger scope, namely the context in which it
was defined:
(def times-two
(let [x 2]
(fn [y] (* y x))))
The fn form defines a function and uses def to store it in a Var named times-two.
The let forms a lexical scope in which the function was defined, so the function gains
access to all the locals in that lexical context. That’s what makes this function a clo-
sure: it uses the local x that was defined outside the body of the function, and so the
Download from Wow! eBook <www.wowebook.com>
136
CHAPTER 7 Functional programming
local and its value become a property of the function itself. The function is said to close
over the local6 x, as in the following example:
(times-two 5)
;=> 10
This isn’t terribly interesting, but one way to make a more exciting closure is to have it
close over something mutable:
(def add-and-get
(let [ai (java.util.concurrent.atomic.AtomicInteger.)]
(fn [y] (.addAndGet ai y))))
(add-and-get 2)
;=> 2
(add-and-get 2)
;=> 4
(add-and-get 7)
;=> 11
The java.util.concurrent.atomic.AtomicInteger class simply holds an integer
value, and its .addAndGet method adds to its value, stores the result, and also returns
the result. The function add-and-get is holding onto the same instance of Atomic-
Integer, and each time it’s called, the value of that instance is modified. Unlike the
earlier times-two function, this one can’t be rewritten with the local ai defined inside
the function. If you tried, each time the function was called, it would create a new
instance with a default value of 0 to be created and stored in ai—clearly not what
should happen. A point of note about this technique is that when closing over some-
thing mutable, you run the risk of making your functions impure and thus more diffi-
cult to test and reason about, especially if the mutable local is shared.
FUNCTIONS RETURNING CLOSURES
Each of the previous examples created a single closure, but by wrapping similar code
in another function definition, you can create more closures on demand. For exam-
ple, we could take the earlier times-two example and generalize it to take an argu-
ment instead of using 2 directly:
(defn times-n [n]
(let [x n]
(fn [y] (* y x))))
We’ve covered functions returning functions before, but if you’re not already familiar
with closures, this may be a stretch. We now have an outer function stored in a Var
named times-n—note we’ve used defn instead of def. When times-n is called with an
argument, it’ll return a new closure created by the fn form and closing over the local
x. The value of x for this closure will be whatever was passed in to times-n. Thus when
we call this returned closure with an argument of its own, it’ll return the value of y
times x, as shown:
6 Locals like x in this example are sometimes called free variables. We don’t use the term because Clojure locals
are immutable.
Download from Wow! eBook <www.wowebook.com>
Closures
137
(times-n 4)
;=> #<user$times_n$fn__39 user$times_n$fn__39@427be8c2>
Viewing the function form for this closure isn’t too useful, so instead we can store it in
a Var, allowing us to call it by a friendlier name such as times-four:
(def times-four (times-n 4))
Here we’re using def again simply to store what times-n returns—a closure over the
number 4:
(times-four 10)
;=> 40
Note that when calling the closure stored in times-four, it used the local it had closed
over as well as the argument in the call.
CLOSING OVER PARAMETERS
In our definition of times-n, we created a local x using let and closed over that
instead of closing over the argument n directly. But this was only to help focus the dis-
cussion on other parts of the function. In fact, closures close over parameters of outer
functions in exactly the same way as they do over let locals. Thus times-n could be
defined without any let at all:
(defn times-n [n]
(fn [y] (* y n)))
All of the preceding examples would work exactly the same. Here’s another function
that creates and returns a closure in a similar way. Note again that the inner function
maintains access to the outer parameter even after the outer function has returned:
(defn divisible [denom]
(fn [num]
(zero? (rem num denom))))
We don’t have to store a closure in a Var, but can instead create one and call it
immediately:
((divisible 3) 6)
;=> true
((divisible 3) 7)
;=> false
Instead of storing or calling a closure, a particular need is best served by passing a clo-
sure along to another function that will use it.
PASSING CLOSURES AS FUNCTIONS
We’ve shown many examples in previous chapters of higher-order functions built in to
Clojure’s core libraries. What we’ve glossed over so far is that anywhere a function is
expected, a closure can be used instead. This has dramatic consequences for how
powerful these functions can be.
Download from Wow! eBook <www.wowebook.com>
138
CHAPTER 7 Functional programming
For example, filter takes a function (called a predicate in this case) and a sequence,
applies the predicate to each value of the sequence,7 and returns a sequence of the just
the values for which the predicate returned something truthy. A simple example of its
use would be to return only the even numbers from a sequence of numbers:
(filter even? (range 10))
;=> (0 2 4 6 8)
Note that filter only ever passes a single argument to the predicate given it. Without
closures, this might be restrictive, but with them we can simply close over the values
needed:
(filter (divisible 4) (range 10))
;=> (0 4 8)
It’s common to define a closure right on the spot where it’s used, closing over what-
ever local-context is needed, as shown:
(defn filter-divisible [denom s]
(filter (fn [num] (zero? (rem num denom))) s))
(filter-divisible 4 (range 10))
;=> (0 4 8)
This kind of on-the-spot anonymous function definition is desired frequently enough
that Clojure spends a little of its small syntax budget on the reader feature to make
such cases more succinct. This #() form was first introduced in chapter 2, and in this
case could be used to write the definition of filter-divisible as
(defn filter-divisible [denom s]
(filter #(zero? (rem % denom)) s))
(filter-divisible 5 (range 20))
;=> (0 5 10 15)
Though certainly more succinct than the extended anonymous function form and the
earlier example using a separate divisible function with filter, there’s a fine line to
balance between reuse8 and clarity. Thankfully, in any case the performance differ-
ences among the three choices are nominal.
SHARING CLOSURE CONTEXT
So far, the closures we’ve shown have stood alone, but it’s sometimes useful to have
multiple closures closing over the same values. This may take the form of an ad hoc
set of closures in a complex lexical environment, such as event callbacks or timer han-
dlers in a nested GUI builder. Or it may be a tidy, specifically designed bundle of val-
ues and related functions—something that can be thought of as an object.
7
Please don’t construe from this wording that filter always iterates through the whole input sequence. Like
most of the seq library, it’s lazy and only consumes as much of the input sequence as needed to produce the
values demanded of it.
8
By hiding divisible as an anonymous function inside filter-divisible, we reduce the reusability of this
code with no real benefit. Anonymous functions are best reserved for when the lexical context being closed
over is more complex or the body of the function too narrow in use to warrant being its own named function.
Download from Wow! eBook <www.wowebook.com>
Closures
139
To demonstrate this, we’ll build a robot object that has functions for moving it
around a grid based on its current position and bearing. For this we need a list of
coordinate deltas for compass bearings, starting with north and going clockwise:
(def bearings [{:x 0, :y 1} ; north
{:x 1, :y 0} ; east
{:x 0, :y -1} ; south
{:x -1, :y 0}]) ; west
Note that this is on a grid where y increases as you go north and x increases as you go
east—mathematical coordinate style rather than spreadsheet cells.
With this in place, it’s easy to write a function forward that takes a coordinate and
a bearing, and returns a new coordinate having moved forward one step in the direc-
tion of the bearing:
(defn forward [x y bearing-num]
[(+ x (:x (bearings bearing-num)))
(+ y (:y (bearings bearing-num)))])
Starting with a bearing of 0 (north) at 5,5 and going one step brings the bot to 5,6:
(forward 5 5 0)
;=> [5 6]
We can also try starting at 5,5 and with bearing 1 (east) or bearing 2 (south) and see
the desired results:
(forward 5 5 1)
;=> [6 5]
(forward 5 5 2)
;=> [5 4]
But we have no closures yet, so we’ll build a bot object that keeps not just its coordi-
nates, but also its bearing. In the process, we’ll move this standalone forward function
into the bot object itself. By making this a closure, we’ll also open up possibilities for
polymorphism later. So here’s a bot that knows how to move itself forward:
(defn bot [x y bearing-num]
{:coords [x y]
:bearing ([:north :east :south :west] bearing-num)
:forward (fn [] (bot (+ x (:x (bearings bearing-num)))
(+ y (:y (bearings bearing-num)))
bearing-num))})
We can create an instance of this bot and query it for its coordinates or its bearing:
(:coords (bot 5 5 0))
;=> [5 5]
(:bearing (bot 5 5 0))
;=> :north
But now that we’ve moved the forward function inside, we no longer pass in parame-
ters, because it gets everything it needs to know from the state of the bot that it closes
Download from Wow! eBook <www.wowebook.com>
140
CHAPTER 7 Functional programming
over. Instead, we use :forward to fetch the closure from inside the bot object and
then use an extra set of parentheses to invoke it with no arguments:
(:coords ((:forward (bot 5 5 0))))
;=> [5 6]
So now we have a somewhat complicated beastie but still only a single closure in the
mix. To make things more interesting, we’ll add turn-left and turn-right9 func-
tions, and store them right there in the object with :forward:
(defn bot [x y bearing-num]
{:coords [x y]
:bearing ([:north :east :south :west] bearing-num)
:forward (fn [] (bot (+ x (:x (bearings bearing-num)))
(+ y (:y (bearings bearing-num)))
bearing-num))
:turn-right (fn [] (bot x y (mod (+ 1 bearing-num) 4)))
:turn-left (fn [] (bot x y (mod (- 1 bearing-num) 4)))})
(:bearing ((:forward ((:forward ((:turn-right (bot 5 5 0))))))))
;=> :east
(:coords ((:forward ((:forward ((:turn-right (bot 5 5 0))))))))
;=> [7 5]
We won’t talk about the verbosity of using the bot object yet, and instead focus on
the features leveraged in the definition of bot itself. We’re freely mixing values com-
puted when a bot is created (such as the :bearing) and functions that create values
when called later. The functions are in fact closures, and each has full access to the
lexical environment. The fact that there are multiple closures sharing the same envi-
ronment isn’t awkward or unnatural and flows easily from the properties of closures
already shown.
We’d like to demonstrate one final feature of this pattern for building objects:
polymorphism. For example, here’s the definition of a bot that supports all of the
same usage as earlier, but this one has its wires crossed or perhaps is designed to work
sensibly in Alice’s Wonderland. When told to go forward it instead reverses, and it
turns left instead of right and vice versa:
(defn mirror-bot [x y bearing-num]
{:coords [x y]
:bearing ([:north :east :south :west] bearing-num)
:forward (fn [] (mirror-bot (- x (:x (bearings bearing-num)))
(- y (:y (bearings bearing-num)))
bearing-num))
:turn-right (fn [] (mirror-bot x y (mod (- 1 bearing-num) 4)))
:turn-left (fn [] (mirror-bot x y (mod (+ 1 bearing-num) 4)))})
9
The :turn-right function uses (+ 1 foo), even though in general (inc foo) would be more idiomatic.
Here it helps highlight to anyone reading the symmetry between turn-right and turn-left. In this case,
using + is more readable than using inc and so is preferred.
Download from Wow! eBook <www.wowebook.com>
Thinking recursively
141
By bundling the functions that operate on data inside the same structure as the data
itself, simple polymorphism is possible. Because each function is a closure, no object
state needs to be explicitly passed; instead, each function uses any locals required to
do its job.
It’s likely you cringed at the number of parentheses required to call these particu-
lar object closures, and rightfully so. We encourage you to extrapolate from the clo-
sure examples when dealing with your own applications, and see how they can solve a
variety of tricky and unusual problems. Although this kind of structure is simple and
powerful10 and may be warranted in some situations, Clojure provides other ways of
associating functions with data objects that are more flexible. In fact, the desire to
avoid a widespread need for this type of ad hoc implementation has motivated Clo-
jure’s reify macro, which we’ll cover in section 9.3.
COMPILE-TIME VERSUS RUN-TIME
When looking at code that includes a closure, it’s not immediately obvious how the
work is distributed between compile-time and run-time. In particular, when you see a
lot of code or processor-intensive work being done in a closure, you might wonder
about the cost of calling the function that creates the closure:
(defn do-thing-builder [x y z]
(fn do-thing [a b]
...
(massive-calculation x y z)
...))
But you don’t need to worry. When this whole expression is compiled, bytecode for
the bodies of do-thing and do-thing-builder are generated and stored in memory.11
In current versions of Clojure, each function definition gets its own class. But when
do-thing-builder is called, it doesn’t matter how large or slow the body of do-thing
is—all that’s done at run-time is the creation of an instance of do-thing’s class. This is
lightweight and fast. Not until the closure returned by do-thing-builder is called does
the complexity or speed of the body of that inner function matter at all.
In this section, you learned that closures are functions that close over lexical locals,
how to create them from inside other functions, how to pass them around and call
them, and even how to build lightweight objects using them. Next, we’ll take a look at
how functions and closures behave when they call themselves, a pattern lovingly
known as recursion.
7.3
Thinking recursively
You’re likely already familiar with the basics of recursion, and as a result can take heart
that we won’t force you to read a beginner’s tutorial again. But because recursive
10 ...a fact any sufficiently experienced JavaScript programmer would be able to confirm.
11 If the code is being compiled ahead of time by the compile function, the generated bytecode is also written
to disk in .class files.
Download from Wow! eBook <www.wowebook.com>
142
CHAPTER 7 Functional programming
solutions are prevalent in Clojure code, it’s important that we cover it well enough that
you can fully understand Clojure’s recursive offerings.
Recursion is often viewed as a low-level operation reserved for times when solu-
tions involving higher-order functions either fail or lead to obfuscation. Granted, it’s
fun to solve problems recursively because even for those of us who’ve attained some
level of acumen with functional programming, finding a recursive solution still injects
a bit of magic into our day. Recursion is a perfect building block for creating higher-
level looping constructs and functions, which we’ll show in this section.
7.3.1
Mundane recursion
A classically recursive algorithm is that of calculating some base number raised to an
exponent, or the pow function. A straightforward12 way to solve this problem recur-
sively is to multiply the base by each successively smaller value of the exponent, as
implemented in the following listing.
Listing 7.4 A version of pow using mundane recursion
(defn pow [base exp]
(if (zero? exp)
1
(* base (pow base (dec exp)))))
(pow 2 10)
;=> 1024
(pow 1.01 925)
;=> 9937.353723241924
We say that the recursive call is mundane 13 because it’s named explicitly rather than
through mutual recursion or implicitly with the recur special form. Why is this a prob-
lem? The answer lies in what happens when we try to call pow with a large value:
(pow 2 10000)
; java.lang.StackOverflowError
The implementation of pow is doomed to throw java.lang.StackOverflowError
because the recursive call is trapped by the multiplication operation. The ideal solu-
tion would be a tail-recursive version that uses the explicit recur form, thus avoiding
stack consumption and the resulting exception. One way to remove the mundane
recursive call is to perform the multiplication at a different point, thus freeing the
recursive call to occur in the tail position, as shown in the next listing.
Listing 7.5 A version of pow using tail recursion, accumulator, and helper function
(defn pow [base exp]
(letfn [(kapow [base exp acc]
12 Yes, we’re aware of Math/pow.
13 Typically mundane recursion is referred to as linear, or the case where the space requirements needed to per-
form the recursion is proportional to the magnitude of the input.
Download from Wow! eBook <www.wowebook.com>
Thinking recursively
143
(if (zero? exp)
acc
(recur base (dec exp) (* base acc))))]
(kapow base exp 1)))
(pow 2 10000)
;=> ... A very big number
This new version of pow uses two common techniques for converting mundane recur-
sion to tail recursion. First, it uses a helper function kapow that does the majority of
the work. Second, kapow itself uses an accumulator acc that holds the result of the
multiplication. The exponent exp is no longer used as a multiplicative value but
instead functions as a decrementing counter, eliminating a stack explosion.
REGULAR RECURSION IS FUN AGAIN WITH LAZY-SEQ
As mentioned in section 6.3, the lazy-seq recipe rule of thumb #1 states that you
should wrap your outer layer function bodies with the lazy-seq macro when generat-
ing lazy seqs. The implementation of lz-rec-step used mundane recursion but man-
aged to avoid stack overflow exceptions thanks to the use of lazy-seq. For functions
generating sequences, the use of lazy-seq might be a better choice than tail recur-
sion, because often the regular (mundane) recursive definition is the most natural
and understandable.
7.3.2
Tail calls and recur
In a language such as Clojure, where function locals are immutable, the benefit of tail
recursion is especially important for implementing algorithms that require the con-
sumption of a value or the accumulation of a result. Before we get deeper into imple-
menting tail recursion, we’ll take a moment to appreciate the historical
underpinnings of tail-call recursion and expound on its further role within Clojure.
GENERALIZED TAIL-CALL OPTIMIZATION
In the Lambda Papers, Guy L. Steele and Gerald Sussman describe their experiences
with the research and implementation of the early versions of the Scheme program-
ming language. The first versions of the interpreter served as a model for Carl
Hewitt’s Actor model (Hewitt 1973) of concurrent computation, implementing both
actors and functions. One day, while eating Ho-Hos,14 Steele and Sussman noticed
that the implementation of control flow within Scheme, implemented using actors,
always ended with one actor calling another in its tail position with the return to the
callee being deferred. Armed with their intimate knowledge of the Scheme compiler,
Steele and Sussman were able to infer that because the underlying architecture deal-
ing with actors and functions was the same, retaining both was redundant. Therefore,
actors were removed from the language and functions remained as the more general
construct. Thus, generalized tail-call optimization was thrust (Steele 1977) into the
world of computer science.
14 This isn’t true, but wouldn’t it be great if it were?
Download from Wow! eBook <www.wowebook.com>
144
CHAPTER 7 Functional programming
Generalized tail-call optimization as found in Scheme (Abelson 1996) can be
viewed as analogous to object delegation. Hewitt’s original actor model was rooted
heavily in message delegation of arbitrary depth, with data manipulation occurring at
any and all levels along the chain. This is similar to an adapter, except that there’s an
implicit resource management element involved. In Scheme, any tail call15 from a
function A to a function B results in the deallocation of all of A local resources and the
full delegation of execution to B. As a result of this generalized tail-call optimization,
the return to the original caller of A is directly from B instead of back down the call
chain through A again, as shown in figure 7.1.
Unfortunately for Clojure, neither the Java Virtual Machine nor its bytecode pro-
vide generalized tail-call optimization facilities. Clojure does provide a tail call special
form recur, but it only optimizes the case of a tail-recursive self-call and not the gener-
alized tail call. In the general case, there’s currently no way to reliably optimize
(Clinger 1998) tail calls.
TAIL RECURSION
The following function calculates the greatest common denominator of two numbers:
(defn gcd [x y]
(cond
(> x y) (gcd (- x y) y)
(< x y) (gcd x (- y x))
:else x))
no TCO
general TCO
caller
caller
(A
( )
A
(A)
(let [a 9]
(let [a 9]
(B))
(B))
dealloc A's
resources
(B)
(B)
; do stuff
; do stuff
; done
; done
de
d a
e ll
l o
l c
o
c B'
B s
'
dealloc B's
re
r s
e ou
o r
u c
r e
c s
resources
(A)
caller
; done
de
d al
a l
l o
l c
o A
'
A s
'
re
r so
s u
o r
u c
r es
e
caller
Figure 7.1 Generalized tail-call optimization: if you know that A calls B in the tail
position, then you also know that A’s resources are no longer needed, allowing
Scheme to deallocate them and defer to B for the return call instead.
15 Bear in mind that in this scenario, A and B can be different functions or the same function.
Download from Wow! eBook <www.wowebook.com>
Thinking recursively
145
The implementation of gcd is straightforward, but you’ll notice that we used mun-
dane recursion instead of tail recursion via recur. In a language such as Scheme con-
taining generalized tail-call optimization, the recursive calls will be optimized
automatically. On the other hand, because of the JVM’s lack of tail-call optimization,
the recur would be needed in order to avoid stack overflow errors.
Using the information in table 7.1, you can replace the mundane recursive calls
with the recur form, causing gcd to be optimized by Clojure’s compiler.
Table 7.1 Tail positions and recur targets
Form(s)
Tail position
recur target?
fn, defn
(fn [args] expressions tail)
Yes
loop
(loop [bindings] expressions tail)
Yes
let, letfn, binding
(let [bindings] expressions tail)
No
do
(do expressions tail)
No
if, if-not
(if test then tailelse tail)
No
when, when-not
(when test expressions tail)
No
cond
(cond test test tail ... :else else tail)
No
or, and
(or test test ... tail)
No
case
(case const const tail ... default tail)
No
WHY RECUR?
If you think that you understand why Clojure provides an explicit tail-call optimization
form rather than an implicit one, then go ahead and skip to the next section.
There’s no technical reason why Clojure couldn’t automatically detect and opti-
mize recursive tail calls—Scala does this—but there are valid reasons why Clojure
doesn’t.
First, because there’s no generalized TCO in the JVM, Clojure can only provide a
subset of tail-call optimizations: the recursive case and the mutually recursive case (see
the next section). By making recur an explicit optimization, Clojure doesn’t give the
pretense of providing full TCO.
Second, having recur as an explicit form allows the Clojure compiler to detect
errors caused by an expected tail call being pushed out of the tail position. If we
change gcd to always return an integer, then an exception is thrown because the
recur call is pushed out of the tail position:
(defn gcd [x y]
(int
(cond
(> x y) (recur (- x y) y)
(< x y) (recur x (- y x))
:else x)))
; java.lang.UnsupportedOperationException: Can only recur from tail position
Download from Wow! eBook <www.wowebook.com>
146
CHAPTER 7 Functional programming
With automatic recursive tail-call optimization, the addition of an outer int call
wouldn’t necessarily trigger (Wampler 2009)16 an error condition. But Clojure
enforces that a call to recur be in the tail position. This benefit will likely cause recur
to live on, even should the JVM acquire TCO.
The final benefit of recur is that it allows the forms fn and loop to act as anony-
mous recursion points.
Why recur indeed.
7.3.3
Don’t forget your trampoline
We touched briefly on the fact that Clojure can also optimize a mutually recursive
function relationship, but like the tail-recursive case, it’s done explicitly. Mutually
recursive functions are nice for implementing finite state machines (FSA), and in this
section we’ll show an example of a simple state machine modeling the operation of an
elevator (Mozgovoy 2009) for a two-story building. There are only four states that the
elevator FSA allows: on the first floor with the doors open or closed and on the second
floor with the door open or closed. The elevator can also take four distinct com-
mands: open doors, close doors, go up, and go down. Each command is only valid in a
certain context; for example, the close command is only valid when the elevator door
is open. Likewise, the elevator can only go up when on the first floor and only down
when on the second floor, and the door must be shut in both instances.
We can directly translate these states and transitions into a set of mutually recursive
functions by associating the states as a set of functions ff-open, ff-closed, sf-
closed, and sf-open, and the transitions :open, :close, :up, and :down, as conditions
for calling the next function. We’d like to create a function elevator that starts in the
ff-open state, takes a sequence of commands, and returns true or false if they corre-
spond to a legal schedule according to the FSA. For example, the sequence [:close
:open :done] would be legal, if not pointless, whereas [:open :open :done] wouldn’t
be legal, because an open door can’t be reopened. The function elevator could be
implemented as shown next.
Listing 7.6 Using mutually recursive functions to implement a finite state machine
(defn elevator [commands]
(letfn
Local functions
[(ff-open [[cmd & r]]
1st floor open
"When the elevator is open on the 1st floor
it can either close or be done."
#(case cmd
:close (ff-closed r)
:done true
false))
(ff-closed [[cmd & r]]
1st floor closed
"When the elevator is closed on the 1st floor
it can either open or go up."
16 The Scala 2.8 compiler recognizes a @tailrec annotation and triggers an error whenever a marked function
or method can’t be optimized.
Download from Wow! eBook <www.wowebook.com>
Thinking recursively
147
#(case cmd
:open (ff-open r)
:up (sf-closed r)
false))
(sf-closed [[cmd & r]]
2nd floor closed
"When the elevator is closed on the 2nd floor
it can either go down or open."
#(case cmd
:down (ff-closed r)
:open (sf-open r)
false))
(sf-open [[cmd & r]]
2nd floor open
"When the elevator is open on the 2nd floor
it can either close or be done"
#(case cmd
:close (sf-closed r)
:done true
false))]
(trampoline ff-open commands)))
Trampoline call
Using letfn in this way allows you to create local functions that reference each other,
whereas (let [ff-open #(...)] ...) wouldn’t, because it executes its bindings seri-
ally. Each state function contains a case macro that dispatches to the next state based
on a contextually valid command. For example, the sf-open state will transition to the
sf-closed state given a :close command, will return true on a :done command (cor-
responding to a legal schedule), or will otherwise return false. Each state is similar in
that the default case command is to return false indicating an illegal schedule. One
other point of note is that each state function returns a function returning a value
rather than directly returning the value. This is done so that the trampoline function
can manage the stack on the mutually recursive calls, thus avoiding cases where a long
schedule would blow the stack. Here’s the operation of elevator given a few example
schedules:
(elevator [:close :open :close :up :open :open :done])
;=> false
(elevator [:close :up :open :close :down :open :done])
;=> true
;; run at your own risk!
(elevator (cycle [:close :open]))
; ... runs forever
Like the recur special form, the trampoline for mutual recursion has a definitive syntactic
and semantic cost on the structure of your code. But whereas the call to recur could be
replaced by mundane recursion without too much effect, save for at the edges, the rules
for mutual recursion aren’t general. Having said that, the actual rules are simple:
1
Make all of the functions participating in the mutual recursion return a func-
tion instead of their normal result. Normally this is as simple as tacking a # onto
the front of the outer level of the function body.
2
Invoke the first function in the mutual chain via the trampoline function.
Download from Wow! eBook <www.wowebook.com>
























148
CHAPTER 7 Functional programming
(elevator)
true
:done
:close
:up
:open
e
n
:close
ff-open
ff-closed
s S
f-closed
sf-open
s S
f-closed
... ff-open
2
2
Figure 7.2 Elevator trampoline: the trampoline function explicitly bounces between
mutually recursive calls.
The final example won’t cause a stack overflow because the trampoline function han-
dles the process of the self calls through the placement of the functions within a list
where each function is “bounced” back and forth explicitly, as seen in figure 7.2.
The typical use case for mutually recursive functions are state machines, of which
the elevator FSA is only a simple case.
7.3.4
Continuation-passing style
Before wrapping up this chapter, we’re going to take time to talk about a style of pro-
gramming not necessarily prevalent in Clojure, but moreso in the functional tradi-
tion: continuation-passing style. Continuation-passing style (CPS) is a hybrid between
recursion and mutual recursion, but with its own set of idioms. We won’t give you a
deep survey of CPS, but this subsection should provide a reasonable overview for
deeper exploration, should you be so inclined.
The nutshell version of CPS is that it’s a way of generalizing a computation (Fried-
man 2001) by viewing it in terms of up to three functions:
An accept function that decides when a computation should terminate
A return continuation that’s used to wrap the return values
A continuation function used to provide the next step in the computation
There’s a reason why many sources on CPS will use the factorial function as a base
example: because it’s exceptionally illustrative, as we show next.
Listing 7.7 Factorial function using continuation-passing style
(defn fac-cps [n k]
(letfn [(cont [v] (k (* v n)))]
Next continuation
(if (zero? n)
Accept function
(k 1)
Return continuation
(recur (dec n) cont))))
(defn fac [n]
(fac-cps n identity))
(fac 10)
;=> 3628800
Though this approach is definitely different than the normal functional structure, it’s
not exactly interesting in and of itself. The power of CPS is that you can extract more
generic function builders using CPS. One such builder, shown in the following listing,
Download from Wow! eBook <www.wowebook.com>
Putting it all together: A* pathfinding
149
can be used to make a range of functions that happen to fall into the same mold of a
mathematical folding function.
Listing 7.8 Continuation-passing style function generator
(defn mk-cps [accept? end-value kend kont]
(fn [n]
((fn [n k]
(let [cont (fn [v] (k (kont v n)))]
Next continuation
(if (accept? n)
Accept continuation
(k end-value)
Return continuation
(recur (dec n) cont))))
n kend)))
(def fac (mk-cps zero? 1 identity #(* %1 %2)))
Factorial fn
(fac 10)
;=> 3628800
(def tri (mk-cps zero? 1 dec #(+ %1 %2)))
Triangular fn
(tri 10)
;=> 55
Though this is potentially a powerful technique, there are a number of reasons pre-
venting its widespread adoption in Clojure:
Without generalized tail-call optimization, the number of continuation calls is
bounded by the size of the stack. If your own applications can guarantee a
bounded execution path for the CPS calls, then this may not be a problem in
practice.
In the case of exception handling, CPS can cause the point of failure to bubble
out, especially on deferred computations such as in using delay, future, or
promise.17 In the abstract this may not seem to be a problem, but if your contin-
uation function is supposed to throw the error but an outer layer function is
doing so instead, then bugs might be difficult to track down.
In a language such as Haskell that has ubiquitous lazy evaluation and pure func-
tions, it’s often not necessary to impose a strict order of execution. One way to
impose a strict order of execution is to design your programs along the
continuation-passing style. Though Clojure isn’t entirely lazy, the matter of out-
of-order execution isn’t a factor against CPS. But CPS isn’t conducive to paral-
lelization, which is antithetical to Clojure’s very nature.
7.4
Putting it all together: A* pathfinding
A* is a best-first pathfinding algorithm that maintains a set of candidate paths through
a “world” with the purpose of finding the least difficult (Bratko 2000) path to some
goal. The difficulty (or cost) of a path is garnered by the A* algorithm through the
use of a function, typically named f, that builds an estimate of the total cost from a
17 Clojure’s future and promise will be discussed in detail in chapter 11.
Download from Wow! eBook <www.wowebook.com>
150
CHAPTER 7 Functional programming
start point to the goal. The application of this cost-estimate function f is used to sort
the candidate paths (Hart 1968) in the order most likely to prove least costly.
THE WORLD
To represent the world, we’ll again use a simple 2D matrix representation:
(def world [[ 1 1 1 1 1]
[999 999 999 999 1]
[ 1 1 1 1 1]
[ 1 999 999 999 999]
[ 1 1 1 1 1]])
The world structure is made from the values 1 and 999 respectively, corresponding to
flat ground and cyclopean mountains. What would you assume is the optimal path
from the upper-left corner [0 0] to the lower-right [4 4]? Clearly the optimal (and
only) option is the Z-shaped path around the walls. Implementing an A* algorithm
should fit the bill, but first, we’ll talk a little bit about how to do so.
NEIGHBORS
For any given spot in the world, we need a way to calculate possible next steps. We can
do this brute force for small worlds, but we’d like a more general function. It turns out
if we restrict the possible moves to north, south, east, and west, then any given move is
+/-1 along the x or y axis. Taking advantage of this fact, we can use the neighbors
function from listing 5.1 as shown here:
(neighbors 5 [0 0])
;=> ([1 0] [0 1])
From the upper-left point, the only next steps are y=0, x=1 or y=1, x=0. So now that we
have that, think about how we might estimate the path cost from any given point. A
simple cost estimate turns out to be described as, “from the current point, calculate
the expected cost by assuming we can travel to the right edge, then down to the lower-
right.” An implementation of the h function estimate-cost that estimates the
remaining path cost is shown next.
Listing 7.9 A straight-line h function to estimate remaining path cost
(defn estimate-cost [step-cost-est size y x]
(* step-cost-est
(- (+ size size) y x 2)))
(estimate-cost 900 5 0 0)
;=> 7200
(estimate-cost 900 5 4 4)
;=> 0
From the y-x point [0 0] the cost of travelling 5 right and 5 down given an estimated
single-step cost step-cost-est is 9000. This is a pretty straightforward estimate based
on a straight-line path. Likewise, starting at the goal state [4 4] would cost nothing.
Still needed is the g function used to calculate the cost of the path so far, named
path-cost, which is provided in the following listing.
Download from Wow! eBook <www.wowebook.com>
Putting it all together: A* pathfinding
151
Listing 7.10 A g function used to calculate the cost of the path traversed so far
(defn path-cost [node-cost cheapest-nbr]
Add cheapest
(+ node-cost
neighbor cost,
(:cost cheapest-nbr 0)))
else 0
(path-cost 900 {:cost 1})
;=> 901
Now that we’ve created an estimated cost function and a current cost function, we can
implement a simple total-cost function for f.
Listing 7.11 f function to calculate the estimated cost of the path (+ (g ...) (h ...)) (defn total-cost [newcost step-cost-est size y x]
(+ newcost
(estimate-cost step-cost-est size y x)))
(total-cost 0 900 5 0 0)
;=> 7200
(total-cost 1000 900 5 3 4)
;=> 1900
The second example shows that if we’re one step away with a current cost of 1000,
then the total estimated cost will be 1900, which is expected. So now we have all of the
heuristic pieces in place. You may think that we’ve simplified the heuristic needs of
A*, but in fact this is all that there is to it. The actual implementation is complex,
which we’ll tackle next.
7.4.1
The A* implementation
Before we show the implementation of A*, we need one more auxiliary function min-
by, used to retrieve from a collection the minimum value dictated by some function.
The implementation of min-by would naturally be a straightforward higher-order
function, as shown:
(defn min-by [f coll]
(when (seq coll)
(reduce (fn [min this]
(if (> (f min) (f this)) this min))
coll)))
(min-by :cost [{:cost 100} {:cost 36} {:cost 9}])
;=> {:cost 9}
This function will come in handy when we want to grab the cheapest path deter-
mined by the cost heuristic. We’ve delayed enough! We’ll finally implement the A*
algorithm so that we navigate around the world. The following listing shows a tail-
recursive solution.
Download from Wow! eBook <www.wowebook.com>
152
CHAPTER 7 Functional programming
Listing 7.12 The main A* algorithm
(defn astar [start-yx step-est cell-costs]
(let [size (count cell-costs)]
(loop [steps 0
routes (vec (replicate size (vec (replicate size nil))))
work-todo (sorted-set [0 start-yx])]
Grab
(if (empty? work-todo)
Check done
first
[(peek (peek routes)) :steps steps]
route
(let [[_ yx :as work-item] (first work-todo)
Get next work item
rest-work-todo (disj work-todo work-item)
nbr-yxs (neighbors size yx)
Clear from todo
cheapest-nbr (min-by :cost
Calc least-cost
(keep #(get-in routes %)
nbr-yxs))
Calc path
newcost (path-cost (get-in cell-costs yx)
so far
cheapest-nbr)
oldcost (:cost (get-in routes yx))]
Check if new
(if (and oldcost (>= newcost oldcost))
is worse
(recur (inc steps) routes rest-work-todo)
(recur (inc steps)
Place new path
(assoc-in routes yx
in routes
{:cost newcost
:yxs (conj (:yxs cheapest-nbr [])
yx)})
(into rest-work-todo
Add estimated path
(map
to todo and recur
(fn [w]
(let [[y x] w]
[(total-cost newcost step-est size y x) w]))
nbr-yxs)))))))))
The main thrust of the astar function occurs at the check that (>= newcost oldcost).
Once we’ve calculated the newcost (the cost so far for the cheapest neighbor) and a
cost-so-far oldcost, we perform one of two actions. The first action occurs when the
newcost is greater than or equal to the oldcost and is to throw away this new path,
because it’s clearly a worse alternative. The other action is the core functionality corre-
sponding to the constant sorting of the work-todo, based on the cost of the path as
determined by the heuristic function total-cost. The soul of A* is based on the fact
that the potential paths stored in work-todo are always sorted and distinct (through
the use of a sorted set), based on the estimated path cost function. Each recursive
loop through the astar function maintains the sorted routes based on the current
cost knowledge of the path, added to the estimated total cost.
The results given by the astar function for the Z-shaped world are shown in the
next listing.
Download from Wow! eBook <www.wowebook.com>

Putting it all together: A* pathfinding
153
Listing 7.13 Running the A* algorithm on the Z World
(astar [0 0]
900
world)
;=> [{:cost 17,
:yxs [[0 0] [0 1] [0 2] [0 3] [0 4] [1 4] [2 4]
[2 3] [2 2] [2 1] [2 0] [3 0] [4 0] [4 1]
[4 2] [4 3] [4 4]]}
:steps 94]
By following the y-x indices, you’ll notice that the
astar function traverses the Z World along the path
where cost is 1, as seen in figure 7.3.
We can also build another world, as shown next,
called Shrubbery World that contains a single weakling
Figure 7.3 A graphical
shrubbery at position [0 3], represented by the num-
representation of Z World clearly
ber 2, and see how astar navigates it.
shows the optimal/only path.
Listing 7.14 The Shrubbery World
(astar [0 0]
900
[[ 1 1 1 2 1]
The shrubbery is 2
[ 1 1 1 999 1]
[ 1 1 1 999 1]
[ 1 1 1 999 1]
[ 1 1 1 1 1]])
The clear path
;=> [{:cost 9,
:yxs [[0 0] [0 1] [0 2] [1 2] [2 2] [3 2]
[4 2] [4 3] [4 4]]}
:steps 134]
When tracing the best path, you will see that the astar function prefers the nonshrub-
bery path. But what would happen if we placed a man-eating bunny along the previ-
ously safe path, represented by an ominously large number, as shown next?
Listing 7.15 The bunny world
(astar [0 0]
900
The shrubbery
[[ 1 1 1 2 1]
looks inviting
[ 1 1 1 999 1]
[ 1 1 1 999 1]
[ 1 1 1 999 1]
The bunny
[ 1 1 1 666 1]])
lies in wait
;=> [{:cost 10,
:yxs [[0 0] [0 1] [0 2] [0 3] [0 4] [1 4]
[2 4] [3 4] [4 4]]}
:steps 132]
Download from Wow! eBook <www.wowebook.com>
154
CHAPTER 7 Functional programming
As expected, the astar function picks the shrubbery path (2) path instead of the evil
bunny path to reach the final destination.
7.4.2
Notes about the A* implementation
The A* algorithm was implemented as idiomatic Clojure source code. Each of the
data structures, from the sorted set to the tail-recursive astar function, to the higher-
order function min-by, was functional in nature and therefore extensible as a result.
We encourage you to explore the vast array of possible worlds traversable by our A*
implementation and see what happens should you change the heuristic (Dijkstra
1959) functions along the way. Clojure encourages experimentation, and by partition-
ing the solution this way, we’ve enabled you to explore different heuristics.
7.5
Summary
We’ve covered a lot about Clojure’s flavor of functional programming in this chapter,
and you may have noticed that it looks like many others. Clojure favors an approach
where immutable data is transformed through the application of functions. Addition-
ally, Clojure prefers that functions be free of side-effects and referentially transparent
(pure) in order to reduce the complexities inherent in widespread data mutation.
Lexical closures provide a simple yet powerful mechanism for defining functions that
carry around with them the value context in which they were created. This allows cer-
tain information to exist beyond their lexical context, much like a poor-man’s object.
Finally, Clojure is built with this in mind, in that its primary form of iteration is
through tail recursion as a natural result of its focus on immutability.
In the next chapter, we’ll explore the feature most identified with Lisp: macros.
Download from Wow! eBook <www.wowebook.com>
Part 4
Large-scale design
Clojure is a practical language, not an academic one; and in the real
world, programs grow large, change over time, and are confronted with shifting
requirements. In this part, we’ll show how Clojure’s Lisp heritage of “code is
data” can help address these problems. We’ll demonstrate the use of macros,
how to create a fluent builder, the benefits of a language that embraces the Java
platform, and how Clojure addresses the mutability of the real world.
Download from Wow! eBook <www.wowebook.com>
Download from Wow! eBook <www.wowebook.com>
Macros
If you give someone Fortran, he has
Fortran. If you give someone Lisp, he
has any language he pleases.
—Guy Steele
This chapter covers
Data is code is data
Defining control structures
Macros combining forms
Using macros to control symbolic resolution time
Using macros to manage resources
Putting it all together: macros returning functions
Macros are where the rubber of “code is data” meets the road of making programs
simpler and cleaner. To fully understand macros, you need to understand the dif-
ferent times of Clojure, of which macros perform the bulk of their work at compile
time. We’ll start by looking at what it means for code to be data and data to be used
as code. This is the background you’ll need to understand that control structures in
Clojure are built out of macros, and how you can build your own. The mechanics of
macros are relatively simple, and before you’re halfway through this chapter you’ll
157
Download from Wow! eBook <www.wowebook.com>
158
CHAPTER 8 Macros
have learned all you technically need to write your own. Where macros get compli-
cated is when you try to bring theoretical knowledge of them into the real world, so to
help you combat that we’ll lead you on a tour of practical applications of macros.
What kinds of problems do macros solve? To start answering that question, con-
sider Clojure’s -> and ->> macros that return the result of a number of threaded
forms. To understand both versions of the arrow macros, we find it useful to think of
them as an arrow indicating the flow of data from one function to another—the form
(-> 25 Math/sqrt int list) can be read as
(-> 25
1
Take the value 25.
(Math/sqrt)
2
Feed it into the method Math/sqrt.
(i n
i t
n )
t
3
Feed that result into the function int.
l
( i
l s
i t
)
t )
4
Feed that result into the function list.
Graphically, this can be viewed as shown in
Figure 8.1 Arrow macro: each expression
figure 8.1.
is inserted into the following one at compile
time, allowing you to write the whole
It expands into the following expression:
expression inside-out when that feels
more natural.
(list (int (Math/sqrt 25)))
When viewed this way, the -> macro can be said to thread a sequence of forms into
each in turn. This threading can be done within any form and is always stitched in as
the first argument to the outermost expression. On the other hand, the ->> macro
will thread the form as the last argument. Observe how the placement of commas1
works as visual markers for the stitch point:
(-> (/ 144 12) (/ ,,, 2 3) str keyword list)
;=> (:2)
(-> (/ 144 12) (* ,,, 4 (/ 2 3)) str keyword (list ,,, :33))
;=> (:32 :33)
(->> a (+ 5 ,,,) (let [a 5] ,,,))
;=> 10
Using the arrows macro is useful when many sequential operations need to be applied
to a single object. So this is one potential use case for macros: taking one form of an
expression and transforming it into another form. In this chapter, we’ll also look at
using macros to combine forms, change forms, control evaluation and resolution of
arguments, manage resources, and build functions. But first, what does it mean that
Clojure code is data, and why should you care?
8.1
Data is code is data
You’re already familiar with textual representations of data in your programs, at least
with strings, lists, vectors, maps, and so on. Clojure, like other Lisps, takes this one
step further by having programs be made entirely out of data. Function definitions in
Clojure programs are also represented using an aggregation of the various data
1 Because commas are considered whitespace. The use here is instructive and not meant as idiomatic.
Download from Wow! eBook <www.wowebook.com>
Data is code is data
159
structures mentioned in the previous chapters. Likewise, the expressions representing
the execution of functions and the use of control structures are also data structures!
These data representations of functions and their executions represent a concept dif-
ferent from the way other programming languages operate. Typically, there’s a sharp
distinction between data structures and functions of the language. In fact, most pro-
gramming languages don’t even remotely describe the form of functions in their tex-
tual representations. With Clojure, there’s no distinction between the textual form
and the actual form of a program. When a program is the data that composes the pro-
gram, then you can write programs to write programs. This may seem like nonsense
now, but as you’ll see throughout this chapter, it’s powerful.
To start with, look at the built-in Clojure function eval, whose purpose is to take a
data structure representing a Clojure expression, evaluate it, and return the result.
This behavior can be seen in the following examples:
(eval 42)
;=> 42
(eval '(list 1 2))
;=> (1 2)
(eval (list 1 2))
; java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.
lang.IFn
Why did we get an exception for the last example? The answer to that lies in the previ-
ous example. The quote in '(list 1 2) causes eval to view it as (list 1 2), which is
the function call to create the resulting list. Likewise, for the final example eval
received a list of (1 2) and attempted to use 1 as a function, thus failing. Not very
exciting, is it? The excitement inherent in eval stems from something that we men-
tioned2 earlier—if you provide eval a list in the form expected of a function call, then
something else should happen. This something else would be the evaluation of a function
call and not of the data structure itself. Look at what happens when we try evaluating
something more complicated:
(eval (list (symbol "+") 1 2))
;=> 3
In words, the steps involved were as follows:
1
The function symbol received a string + and returned a symbol data type of +.
2
The function list received three arguments: a symbol +, the integer 1, and the
integer 2, and returned a list of these elements.
3
The eval function received a list data type of (+ 1 2), recognized it as the func-
tion call form, and executed the + function with the arguments 1 and 2, return-
ing the integer 3.
2 All the way back in section 2.5.
Download from Wow! eBook <www.wowebook.com>
160
CHAPTER 8 Macros
8.1.1
Syntax-quote, unquote, and splicing
In section 1.5.6, we mentioned quoting and its effects on evaluation, and in this chapter
we’ll expand on that theme fully as it relates to Clojure’s macro facility. But the func-
tionality of the quoting forms is orthogonal to macros, and they can be used indepen-
dently. As we show3 in listing 8.1, using quoting and unquoting in a function allows us
to create an evaluation function, contextual-eval, that takes an explicit context map.
Listing 8.1 An implementation of eval taking a local context
(defn contextual-eval [ctx expr]
(eval
Build let bindings
`(let [~@(mapcat (fn [[k v]] [k `'~v]) ctx)]
at compile-time
~expr)))
(contextual-eval {'a 1, 'b 2} '(+ a b))
;=> 3
(contextual-eval {'a 1, 'b 2} '(let [b 1000] (+ a b)))
;=> 1001
Handling nested syntax-quotes
Dealing with nested syntax-quotes can at times be complicated. But you can visualize
the way in which unquoting affects the nested structures as result of repeated eval-
uations (Steele 1990) relative to its nesting level:
(let [x 9, y '(- x)]
(println `y)
(println ``y)
(println ``~y)
(println ``~~y)
(contextual-eval {'x 36} ``~~y))
; user/y
; (quote user/y)
; user/y
; (- x)
;=> -36
The nesting of the syntax-quotes in the first two println calls takes the value of y
further up the abstraction ladder. But by including a single unquote in the third
println, we again bring it back down. Finally, by unquoting a second time, we’ve cre-
ated a structure that can then be evaluated—and doing so yields the result -36. We
had to use contextual-eval in the tail because core eval doesn’t have access to
local bindings—only Var bindings. One final note is that had we attempted to unquote
one extra time, we’d have seen the exception java.lang.IllegalStateExcep-
tion: Var clojure.core/unquote is unbound. The reason for this error is that
unquote is the way to “jump” out of a syntax-quote, and to do so more than nesting
allows will cause an error. We won’t use this technique in this chapter, and in most
cases you won’t need to utilize it unless you’re planning to create macro-defining
macros—something we won’t do until section 13.1.
3 Thanks to George Jahad for the implementation on which contextual-eval is based.
Download from Wow! eBook <www.wowebook.com>
Defining control structures
161
Rarely will you see the use of syntax-quote outside the body of a macro, but there’s
nothing preventing it from being used this way—and doing so is powerful. But the
maximum power of quoting forms is fully realized when used with macros.
Working from a model where code is data, Clojure is able to manipulate structures
into different executable forms at both runtime and compile time. We’ve already
shown how this can be done at runtime using eval and contextual-eval, but this
doesn’t serve the purpose of compile-time manipulation. It probably doesn’t need say-
ing, but because this is a book about Clojure, we will: macros are the way to achieve
this effect.
8.1.2
Macro rules of thumb
Before we begin, we should list a few rules of thumb to observe when writing macros:
Don’t write a macro if a function will do. Reserve macros to provide syntactic
abstractions or create binding forms.
Write an example usage.
Expand your example usage by hand.
Use macroexpand, macroexpand-1, and clojure.walk/macroexpand-all4 liber-
ally to understand how your implementation works.
Experiment at the REPL.
Break complicated macros into smaller functions whenever possible.
Throughout this chapter, you’ll see all of these rules to varying degrees. Obviously,
we’re trying to balance best practices, teaching, and page counts, so we may not always
adhere entirely. Even so, we’ll try to highlight those times when we do break from the
recommended heuristics. Having said that, we’ll talk first about the most ubiquitous
use of macros: creating custom control structures.
8.2
Defining control structures
Most control structures in Clojure are implemented via macros, so they provide a nice
starting point for learning how macros can be useful. Macros can be built with or with-
out using syntax-quote, so we’ll show examples of each.
In languages lacking macros, such as Haskell5 for example, the definition of control
structures relies on the use of higher-order functions such as we showed in section
7.1.2. Though this fact in no way limits the ability to create control structures in
Haskell, the approach that Lisps take to the problem is different. The most obvious
advantage of macros over higher-order functions is that the former manipulate
compile-time forms, transforming them into runtime forms. This allows your programs
4
The macroexpand-all function is a useful debugging aid, as we’ll demonstrate in this chapter. But it’s worth
knowing that unlike the other macroexpand functions, it doesn’t use exactly the same logic as the Clojure
compiler itself, and thus may in some unusual circumstances produced misleading results.
5
Although there’s a GHC extension named Template Haskell that provides a macro-like capability, this isn’t
the norm.
Download from Wow! eBook <www.wowebook.com>
162
CHAPTER 8 Macros
to be written in ways natural to your problem domain, while still maintaining runtime
efficiency. Clojure already provides a rich set of control structures, including but not
limited to doseq, while, if, if-let, and do, but in this section we’ll write a few others.
8.2.1
Defining control structures without syntax-quote
Because the arguments to defmacro aren’t evaluated before being passed to the
macro, they can be viewed as pure data structures, and manipulated and analyzed as
such. Because of this, amazing things can be done on the raw forms supplied to mac-
ros even in the absence of unquoting.
Imagine a macro named do-until that will execute all of its clauses evaluating as
true until it gets one that is falsey:
(do-until
(even? 2) (println "Even")
(odd? 3) (println "Odd")
(zero? 1) (println "You never see me")
:lollipop (println "Truthy thing"))
; Even
; Odd
;=> nil
A good example of this type of macro is Clojure’s core macro cond, which with some
minor modifications can be made to behave differently:
(defmacro do-until [& clauses]
(when clauses
(list `when (first clauses)
(if (next clauses)
(second clauses)
(throw (IllegalArgumentException.
"do-until requires an even number of forms")))
(cons 'do-until (nnext clauses)))))
The first expansion of do-until illustrates how this macro operates:
(macroexpand-1 '(do-until true (prn 1) false (prn 2)))
;=> (when true (prn 1) (do-until false (prn 2)))
do-until recursively expands into a series of when calls, which themselves expand into
a series of if expressions:
(require '[clojure.walk :as walk])
(walk/macroexpand-all '(do-until true (prn 1) false (prn 2)))
;=> (if true (do (prn 1) (if false (do (prn 2) nil))))
(do-until true (prn 1) false (prn 2))
; 1
;=> nil
Now you could write out the nested if structure manually and achieve the same
result, but the beauty of macros lies in the fact that they can do so on your behalf
while presenting a lightweight and intuitive form. In cases where do-until can be
used, it removes the need to write and maintain superfluous boilerplate code. This
Download from Wow! eBook <www.wowebook.com>
Defining control structures
163
idea can be extended to macros in general and their propensity to reduce unneeded
boilerplate for a large category of circumstances, as the programmer desires. One
thing to note about do-until is that it’s meant to be used only for side effects, because
it’s designed to always return nil. Macros starting with do tend to act the same.
8.2.2
Defining control structures using syntax-quote and unquoting
Not all control structures will be as simple as do-until. Instead, there will be times
when you’ll want to selectively evaluate macro arguments, structures, or substructures.
In this section, we’ll explore one such macro named unless, implemented using
unquote and unquote-splice.
Ruby provides a control structure named unless that reverses the sense (Olsen
2007) of a when statement, executing the body of a block when a given condition eval-
uates to false:
(unless (even? 3) "Now we see it...")
;=> "Now we see it..."
(unless (even? 2) "Now we don't.")
;=> nil
The maverick implementation6 of unless as demonstrated previously and as shown in
the following listing is straightforward.
Listing 8.2 A Clojure Implementation of unless
(defmacro unless [condition & body]
`(if (not ~condition)
Unquote condition
(do ~@body)))
Splice body
(defn from-end [s n]
Use our unless
(let [delta (dec (- (count s) n))]
(unless (neg? delta)
Return nil
(nth s delta))))
if negative
(from-end (range 1 101) 10)
;=> 90
The body of the unless implementation uses three features first shown in section
1.5.6: syntax-quote (written as a single back-quote), unquote (written as ~), and
unquote-splice (written as ~@). Syntax-quote allows the if form to act as a template for
the expression that any use of the macro becomes when expanded. The unquote and
splicing-unquote provide the “blanks” where the values for the parameters condition
and body will be inserted.
Because unless relies on the result of a condition for its operation, it’s imperative
that it evaluate the condition part using unquote. If we didn’t use unquote in this
instance, then instead of evaluating a function (even? 3), it would instead attempt to
resolve a namespace Var named condition that may not exist —and if it does exist, it
6 The proper way to define unless is either (defmacro unless [& args] `(when-not ~@args)) or even
(clojure.contrib.def/defalias unless when-not)—or just use when-not from the start.
Download from Wow! eBook <www.wowebook.com>
164
CHAPTER 8 Macros
might be arbitrarily truthy at the time of the macro call. Some of the unintended con-
sequences of this mistake are shown in the next listing.
Listing 8.3 Name capture in unless
(macroexpand `(if (not condition) "got it"))
Missing ~
;=> (if (clojure.core/not user/condition) "got it")
resolves to Var
(eval `(if (not condition) "got it"))
; java.lang.Exception: No such var: user/condition
(def condition false)
Undesired result
(eval `(if (not condition) "got it"))
when bound
;=> "got it"
Clearly this isn’t the desired behavior. Instead, by unquoting the condition local, we
ensure that the function call is used instead. It’s easy to forget to add an unquote to
the body of a macro, and depending on the condition of your runtime environment,
the problem may not be immediately obvious.
8.3
Macros combining forms
Macros are often used for combining a number of forms and actions into one consis-
tent view. This behavior could be seen in the previous section with the do-until
macro, but it’s more general. In this section, we’ll show how macros can be used to
combine a number of tasks in order to simplify an API. Clojure’s defn macro is an
instance of this type of macro because it aggregates the processes of creating a func-
tion, including
Creating the corresponding function object using fn
Checking for and attaching a documentation string
Building the :arglists metadata
Binding the function name to a Var
Attaching the collected metadata
You could perform all of these steps over and over again every time you wanted to cre-
ate a new function, but thanks to macros you can instead use the more convenient
defn form. Regardless of your application domain and its implementation, program-
ming language boilerplate code inevitably occurs. But identifying these repetitive tasks
and writing macros to simplify and reduce or eliminate the tedious copy-paste-tweak
cycle can work to reduce the incidental complexities inherent in a project. Where mac-
ros differ from techniques familiar to proponents of Java’s object-oriented style—
including hierarchies, frameworks, inversion of control, and the like—is that they’re
treated no differently by the language itself. Clojure macros work to mold the language
into the problem space rather than forcing you to mold the problem space into the
constructs of the language. There’s a specific term for this, domain-specific language, but
in Lisp the distinction between DSL and API is thin to the point of transparency.
Envision a scenario where you want to be able to define Vars that call a function
whenever their root bindings change. You could do this using the add-watch function
Download from Wow! eBook <www.wowebook.com>
Using macros to change forms
165
that allows for the attachment of a watcher to a reference type that’s called whenever a
change occurs within. The add-watch function itself takes three arguments: a refer-
ence, a watch function key, and a watch function called whenever a change occurs.
You could enforce that every time someone wants to define a new Var, they must fol-
low these steps:
1
Define the Var.
2
Define a function (maybe inline to save a step) that will be the watcher.
3
Call add-watch with the proper values.
A meager three steps isn’t too cumbersome a task to remember in a handful of uses,
but over the course of a large project it’s easy to forget and/or morph one of these
steps when the need to perform them many times occurs. Therefore, perhaps a better
approach is to define a macro to perform all of these steps for you, as the following
definition does:
(defmacro def-watched [name & value]
`(do
(def ~name ~@value)
(add-watch (var ~name)
:re-bind
(fn [~'key ~'r old# new#]
(println old# " -> " new#)))))
Ignoring symbol resolution and auto-gensym, which we’ll cover in upcoming sections,
the macro called as (def-watched x 2) expands into roughly the following:
(do (def x 2)
(add-watch (var x)
:re-bind
(fn [key r old new]
(println old " -> " new))))
The results of def-watched are thus
(def-watched x (* 12 12))
x
;=> 144
(def x 0)
; 144 -> 0
Lisp programs in general (and Clojure programs specifically) use macros of this sort
to vastly reduce the boilerplate needed to perform common tasks. Throughout this
chapter, you’ll see macros that combine forms, so there’s no need to dwell on the mat-
ter here. Instead, we’ll move on to a macro domain that does just that, with the added
bonus of performing some interesting transformations in the process.
8.4
Using macros to change forms
One way to design macros is to start by writing out example code that you wish
worked—code that has the minimal distance between what you must specify and the
Download from Wow! eBook <www.wowebook.com>
166
CHAPTER 8 Macros
specific application domain in which you’re working. Then, with the goal of making
this code work, you begin writing macros and functions to fill in the missing pieces.
For example, when designing software systems, it’s often useful to identify the
“things” comprising your given application domain, including their logical groupings.
The level of abstraction at this point in the design is best kept high (Rosenberg 2005)
and shouldn’t include details about implementation. Imagine that you want to
describe a simple domain of the ongoing struggle between humans and monsters:
Man versus monster
People
Men (humans)
Name
Have beards?
Monsters
Chupacabra
Eats goats?
Though this is a simple format, it needs work to be programmatically useful. There-
fore, the goal of this section is to write macros performing the steps to get from this
simple representation to the one more conducive to processing. One such structure is
a tree composed of individual generic nodes, each taking a form similar to that shown
in the next listing.
Listing 8.4 Domain DSL’s underlying form
{:tag <node form>,
Domain, grouping
:attrs {},
Name people
:content [<nodes>]}
Properties
You’d never say this is a beautiful format, but it does present practical advantages over
the original format—it’s a tree, it’s composed of simple types, it’s regular, and it’s rec-
ognizable to some existing libraries.
CLOJURE APHORISM Clojure is a design language where the conceptual
model is also Clojure.
We’ll start with the outer-level element, domain:
(defmacro domain [name & body]
`{:tag :domain,
:attrs {:name (str '~name)},
:content [~@body]})
The body of domain is fairly straightforward in that it sets the domain-level tree node
and splices the body of the macro into the :content slot. After domain expands, you’d
expect its body to be composed of a number of grouping forms, which are then han-
dled by the aptly named macro:
Download from Wow! eBook <www.wowebook.com>
Using macros to change forms
167
(declare handle-things)
(defmacro grouping [name & body]
`{:tag :grouping,
:attrs {:name (str '~name)},
:content [~@(handle-things body)]})
Similarly to domain, grouping expands into a node with its body spliced into the :con-
tent slot. But grouping differs from domain in that it splices in the result of the call to
a function handle-things:
(declare grok-attrs grok-props)
(defn handle-things [things]
(for [t things]
{:tag :thing,
:attrs (grok-attrs (take-while (comp not vector?) t))
:content (if-let [c (grok-props (drop-while (comp not vector?) t))]
[c]
[])})))
Because the body of a thing is fairly simple and regular, we can simplify the implemen-
tation of handle-things by again splitting it into two functions. The first function
grok-attrs handles everything within the body of a thing that’s not a vector, and the
other grok-props handles properties that are. In both cases, these leaf-level functions
return specifically formed maps:
(defn grok-attrs [attrs]
(into {:name (str (first attrs))}
(for [a (rest attrs)]
(cond
(list? a) [:isa (str (second a))]
(string? a) [:comment a]))))
The implementation of grok-attrs may seem overly complex, especially given that
the example domain model DSL only allows for a comment attribute and an optional
isa specification. But by laying out this way, we can easily expand the function to han-
dle a richer set of attributes later. Likewise with grok-props, we provide a more com-
plicated function to pull apart the vector representing a property so that it’s more
conducive to expansion:
(defn grok-props [props]
(when props
{:tag :properties, :attrs nil,
:content (apply vector (for [p props]
{:tag :property,
:attrs {:name (str (first p))},
:content nil}))}))
Now that we’ve created the pieces, take a look at the new DSL in action in the follow-
ing listing.
Download from Wow! eBook <www.wowebook.com>
168
CHAPTER 8 Macros
Listing 8.5 Exploring the domain DSL results
(def d
(domain man-vs-monster
(grouping people
Group of people
(Human "A stock human")
One kind of person
(Man (isa Human)
Another kind of person
"A man, baby"
[name]
[has-beard?]))
(grouping monsters
Group of monsters
(Chupacabra
One kind of monster
"A fierce, yet elusive creature"
[eats-goats?]))))
(:tag d)
;=> :domain
(:tag (first (:content d)))
;=> :grouping
Maybe that’s enough to prove to you that we’ve constructed the promised tree, but
probably not. Therefore, we can pass a tree into a function that expects one of that
form7 and see what comes out on the other end:
(use '[clojure.xml :as xml])
(xml/emit d)
Performing this function call will print out the corresponding XML representation,
minus the pretty printing, shown here.
Listing 8.6 An XML transformation of the domain DSL structure
<?xml version='1.0' encoding='UTF-8'?>
<domain name='man-vs-monster'>
(Domain ...)
<grouping name='people'>
(Grouping ...)
<thing name='Human' comment='A stock human'>
(Human ...)
<properties></properties>
</thing>
<thing name='Man' isa='Human' comment='A man, baby'>
(Man ...)
<properties>
<property name='name'/>
<property name='has-beard?'/>
</properties>
</thing>
</grouping>
(Grouping ...)
<grouping name='monsters'>
<thing name='Chupacabra' comment='A fierce, yet elusive creature'>
<properties>
<property name='eats-goats?'/>
</properties>
7
The namespace clojure.contrib.json in the Clojure contrib library also contains some functions that
would be able to handle the domain DSL structure seamlessly. Additionally, Enlive (http://mng.bz/8Hh6)
should also recognize the resultant structure.
Download from Wow! eBook <www.wowebook.com>
Using macros to control symbolic resolution time
169
</thing>
</grouping>
</domain>
Our approach was to define a single macro entry point domain, intended to build the
top-level layers of the output data structure and instead pass the remainder on to aux-
iliary functions for further processing. In this way, the body of the macro expands into
a series of function calls, each taking some subset of the remaining structure and
returning some result that’s spliced into the final result. This functional composition
approach is fairly common when defining macros. The entirety of the domain descrip-
tion could’ve been written within one monolithic macro, but by splitting the responsi-
bilities, you can more easily extend the representations for the constituent parts.
Macros take data and return data, always. It so happens that in Clojure, code is
data and data is code.
8.5
Using macros to control symbolic resolution time
Whereas functions accept and return values that are meaningful to your application
at runtime, macros accept and return code forms that are meaningful at compile
time. Any symbol has some subtleties depending on whether it’s fully qualified or not,
its resolution time, and its lexical context. These factors can be controlled in any par-
ticular case by the appropriate use of quoting and unquoting, which we explore in
this section.
Clojure macros are mostly safe from name capture, in that the use of syntax-quote
in macros is encouraged and idiomatic, and it’ll resolve symbols at macro-expansion
time. This strategy reduces complexity by ensuring that symbols refer to those avail-
able at a known instance rather than to those unknown in the execution context.
For example, consider one of the simplest possible macros: