The Joy of Clojure (book review)

It’s been a long time since Lisp and Scheme have been in the news. Despite the abilities of these languages to enable fast prototyping of complicated applications, they got overlooked in the old days due to people’s suspicion of garbage collection and their rather strange syntax.

In more modern times, the take up of virtual machine technology and associated technologies such as garbage collection has made it possible for some of these older languages to re-emerge. Indeed, not a day goes by without another language appearing which targets the Java Virtual Machine (JVM). One such language is Clojure, which is making quite a splash. Clojure takes some of the ideas behind Lisp and Scheme, mixes in some other ideas from functional programming, and blends this together with a large dose of practicality. The result is a language that runs nicely on top of the JVM and which allows the programmer to interoperate very easily with existing Java libraries and frameworks. 

No more does the Lisp system have to be in charge of the process. Clojure code compiles down to Java class files which can loaded and exercised by a non-Clojure controller. Of course, things are not completely rosy. When you are debugging mixed- language applications, you get to see what the datatypes really look like and see more of the implementation details than you’d really like. However, we are now at a stage where different parts of a problem can be solved by a language which suits that particular problem area, and then these parts can be linked to form a coherent whole.

Lisp has had some notable successes in the past. Paul Graham’s ViaWeb (which was bought by Yahoo! in the early days of the web) is one example, and, more recently, some of the travel scheduling companies have used this language to good effect. Clojure might just be one way of getting these advantages into the hands of the programmer-in-the-office. Lisp is very extensible and Clojure’s creator, Rich Hickey, has used this to add many interesting concurrency constructs into the language. Since multi-core concurrency is the new hot topic, this makes the language even more deserving of consideration by the average programmer. 

The Joy of Clojure is a book which tries to show experienced programmers how Clojure can solve some of the common problems that they have in their day jobs. Section 1.3 of the introduction neatly decomposes the problems with many other languages into the categories: verbosity, unavoidable boiler-plate, a long thought-code-feedback loop, incidental complexity, difficulties in extension, and deficiencies in supporting crucial programming paradigms.  The book does a good job at relating these aspects to the parts of Clojure that help with them, helping to communicate the “why?” instead of just the “how?”

The book starts quickly, and continues at quite a pace. It is full of lots of interesting observations about programming in general and references lots of material from lots of sources. Chapter one, for example, starts out with the three pillars of Clojure – simplicity, freedom to focus and empowerment, and then leaps into some fairly large examples of the language’s extensibility and its support for encapsulation, by way of an embedded Domain Specific Language (DSL) for a SQL query and the implementation of a chessboard. This chapter also does a really good job of explaining the rationale for functional programming via the metaphor of a child’s flip book, and also explains how Clojure can solve the Expression problem using protocols. The only trouble covering material at this pace is that we are left with lots of forward references to the following chapters. This happens frequently throughout the book, and it’s always unclear whether it is worth jumping forwards to follow up on something, or whether we should wait to reach the material. Personally, I like this style, but I imagine some people might find it annoying. The book is definitely worth a couple of reads and on the second pass through the forward referencing isn’t quite as annoying.

Chapter 2 of the book covers the basic datatypes and syntax of the Clojure language, starting with basic strings and integers, and then covering the constructed types of list, vector, map and set. The emphasis on the latter two types is what makes this language different from more mainstream languages. We then move on to defining functions including loops and recursion, and then cover very basic Java interoperability and how namespaces affect the visibility of definitions.

Chapters 3, 4 and 5 go back through the scalars and the composite datatypes in more detail, encouraging the reader to experiment at the REPL, (the read-eval-print-loop), which is the means by which Clojure allows the programmer to experiment with the definitions that they have just made. Programming can be a very exploratory activity, and this is the way Clojure makes this possible.

Sequences in Clojure are a nice unifying concept. Most of the datatypes can be viewed as sequences, and chapter 6 takes this to the next level by explaining laziness and how it allows very succinct definitions of things like the triangle numbers. In this case, rather than defining a function to return the nth triangle number, it is much easier to define a lazy sequence of such numbers and then use the standard sequence functions to select a sub-range. This chapter contains a large worked example of implementing a lazy version of quicksort. This example is fascinating, especially as we can generate the first element of the sorted result by asking for the first element, and letting the laziness infer the work that needs to be done.

Chapter 7 covers functional programming in more depth, concentrating on closures and higher order functions. This chapter finishes with an example implementation of the A*search algorithm. Examples like these are one of the strongest parts of the book. Most chapters have examples that were well chosen, being neither too complicated (which tends to obfuscate the point) nor too easy (which misses the chance to show language features in context).

Chapter 8 covers macros, and does a reasonable job of covering all of the related material, with an emphasis on code-as-data. Again there’s a good example to illustrate the material.

Chapter 9 moves on to more details about namespaces, multi-methods and records and protocols. This was useful material which was well presented, though having such disparate topics in the same chapter felt a little strange.

Chapter 10 covers the interoperability story, showing how easy it is to call Java methods, and how easy it is to expose functionality written in Clojure to the outside world. I’ve used Clojure a fair amount in my spare time and certainly learned some things from this chapter.

Chapter 11, entitled Mutation, covers the stateful side of the Clojure story, covering the software transactional memory (via Refs), agents, atoms and vars, with some additional material on using standard Java locks. There’s also coverage of the primitives for launching parallel tasks as well as futures and promises. Clojure has a big emphasis on stateless programming, but coordinated changes of application state are required to write anything but trivial applications.

Chapter 12 covers performance, detailing how you can aid the compiler with type hints, which allow it to avoid boxing primitive values too frequently. It also covers local mutation in order to improve performance by using transients, and has a good discussion of memorization.

The final chapter, chapter 13, makes some final points about the usefulness of Clojure. It discusses how easy it is to define embedded DSLs, and how some of traditional design patterns can be implemented without requiring a lot of scaffolding.

This is a good book, which explains very well the major parts of the Clojure language, and contrasts the Clojure style against that of more mainstream languages. With such a range of concepts and ideas, the material is fairly well organised, though some chapters don’t really have a single theme running through them. In some cases, the author references other external material, and it is a pity that there isn’t time to go into more depth in the text itself. However, at 416 pages, it’s clear that there just isn’t the space to do this.

The examples concentrate on the language itself. There is nothing to show you how to write database access code in Clojure  or how to hook up to a web server, or even how to use a build tool such as Leiningen to package your Clojure code into a deployable application. By focusing purely on the language instead of its use, the book can go into more technical depth than some other texts.

The Joy of Clojure explains both the “how” and “why” of Clojure, as it promises it will in the introductory chapter, and it is well worth a read. If you haven’t done any Lisp or Scheme before, you might be better off reading a more introductory text first, but I can’t see any book explaining the essence of Clojure better than this one.