The Joy of Clojure, Second Edition
Michael Fogus and Chris Houser
Foreword by William E. Byrd and Daniel P. Friedman
  • May 2014
  • ISBN 9781617291418
  • 520 pages
  • printed in black & white

A cornucopia of programming concepts.

From the Foreword by William E. Byrd and Daniel P. Friedman, authors of "The Reasoned Schemer"

The Joy of Clojure, Second Edition is a deep look at the Clojure language. Fully updated for Clojure 1.6, this new edition goes beyond just syntax to show you the "why" of Clojure and how to write fluent Clojure code. You'll learn functional and declarative approaches to programming and will master the techniques that make Clojure so elegant and efficient.

Table of Contents show full

foreword to the second edition

foreword to the first edition



about this book

about clojure

about the cover illustration

Part 1 Foundations

1. Clojure philosophy

1.1. The Clojure way

1.1.1. Simplicity

1.1.2. Freedom to focus

1.1.3. Empowerment

1.1.4. Clarity

1.1.5. Consistency

1.2. Why a(nother) Lisp?

1.2.1. Beauty

1.2.2. But what’s with all the parentheses?

1.3. Functional programming

1.3.1. A workable definition of functional programming

1.3.2. The implications of functional programming

1.4. Why Clojure isn’t especially object-oriented

1.4.1. Defining terms

1.4.2. Imperative "baked in"

1.4.3. Most of what OOP gives you, Clojure provides

1.5. Summary

2. Drinking from the Clojure fire hose

2.1. Scalars: the base data types

2.1.1. Numbers

2.1.2. Integers

2.1.3. Floating-point numbers

2.1.4. Rationals

2.1.5. Symbols

2.1.6. Keywords

2.1.7. Strings

2.1.8. Characters

2.2. Putting things together: collections

2.2.1. Lists

2.2.2. Vectors

2.2.3. Maps

2.2.4. Sets

2.3. Making things happen: calling functions

2.4. Vars are not variables

2.5. Functions

2.5.1. Anonymous functions

2.5.2. Creating named functions with def and defn

2.5.3. Functions with multiple arities

2.5.4. In-place functions with #()

2.6. Locals, loops, and blocks

2.6.1. Blocks

2.6.2. Locals

2.6.3. Loops

2.7. Preventing things from happening: quoting

2.7.1. Evaluation

2.7.2. Quoting

2.7.3. Unquote

2.7.4. Unquote-splicing

2.7.5. Auto-gensym

2.8. Using host libraries via interop

2.8.1. Accessing static class members (Clojure only)

2.8.2. Creating instances

2.8.3. Accessing instance members with the . operator

2.8.4. Setting instance fields

2.8.5. The .. macro

2.8.6. The doto macro

2.8.7. Defining classes

2.9. Exceptional circumstances

2.9.1. Throwing and catching

2.10. Modularizing code with namespaces

2.10.1. Creating namespaces using ns

2.10.2. Loading other namespaces with :require

2.10.3. Loading and creating mappings with :refer

2.10.4. Creating mappings with :refer

2.10.5. Loading Java classes with :import

2.11. Summary

3. Dipping your toes in the pool

3.1. Truthiness

3.1.1. What’s truth?

3.1.2. Don’t create Boolean objects

3.1.3. nil vs. false

3.2. Nil pun with care

3.3. Destructuring

3.3.1. Your assignment, should you choose to accept it

3.3.2. Destructuring with a vector

3.3.3. Destructuring with a map

3.3.4. Destructuring in function parameters

3.3.5. Destructuring vs. accessor methods

3.4. Using the REPL to experiment

3.4.1. Experimenting with seqs

3.4.2. Experimenting with graphics

3.4.3. Putting it all together

3.4.4. When things go wrong

3.4.5. Just for fun

3.5. Summary

Part 2 Data types

4. On scalars

4.1. Understanding precision

4.1.1. Truncation

4.1.2. Promotion

4.1.3. Overflow

4.1.4. Underflow

4.1.5. Rounding errors

4.2. Trying to be rational

4.2.1. Why be rational?

4.2.2. How to be rational

4.2.3. Caveats of rationality

4.3. When to use keywords

4.3.1. Applications of keywords

4.3.2. Qualifying your keywords

4.4. Symbolic resolution

4.4.1. Metadata

4.4.2. Symbols and namespaces

4.4.3. Lisp-1

4.5. Regular expressions-the second problem

4.5.1. Syntax

4.5.2. Regular-expression functions

4.5.3. Beware of mutable matchers

4.6. Summary

5. Collection types

5.1. Persistence, sequences, and complexity

5.1.1. "You keep using that word. I do not think it means what you think it means."

5.1.2. Sequence terms and what they mean

5.1.3. Big-O

5.2. Vectors: creating and using them in all their varieties

5.2.1. Building vectors

5.2.2. Large vectors

5.2.3. Vectors as stacks

5.2.4. Using vectors instead of reverse

5.2.5. Subvectors

5.2.6. Vectors as map entries

5.2.7. What vectors aren’t

5.3. Lists: Clojure’s code-form data structure

5.3.1. Lists like Lisps like

5.3.2. Lists as stacks

5.3.3. What lists aren’t

5.4. How to use persistent queues

5.4.1. A queue about nothing

5.4.2. Putting things on

5.4.3. Getting things

5.4.4. Taking things off

5.5. Persistent sets

5.5.1. Basic properties of Clojure sets

5.5.2. Keeping your sets in order with sorted-set

5.5.3. The contains? function

5.5.4. The clojure.set namespace

5.6. Thinking in maps

5.6.1. Hash maps

5.6.2. Keeping your keys in order with sorted maps

5.6.3. Keeping your insertions in order with array maps

5.7. Putting it all together: finding the position of items in a sequence

5.7.1. Implementation

5.8. Summary

Part 3 Functional programming

6. Being lazy and set in your ways

6.1. On immutability: being set in your ways

6.1.1. What is immutability?

6.1.2. What is immutability for?

6.2. Structural sharing: a persistent toy

6.3. Laziness

6.3.1. Familiar laziness with logical-and

6.3.2. Understanding the lazy-seq recipe

6.3.3. Losing your head

6.3.4. Employing infinite sequences

6.3.5. The delay and force macros

6.4. Putting it all together: a lazy quicksort

6.4.1. The implementation

6.5. Summary

7. Functional programming

7.1. Functions in all their forms

7.1.1. First-class functions

7.1.2. Higher-order functions

7.1.3. Pure functions

7.1.4. Named arguments

7.1.5. Constraining functions with pre- and postconditions

7.2. On closures

7.2.1. Functions returning closures

7.2.2. Closing over parameters

7.2.3. Passing closures as functions

7.2.4. Sharing closure context

7.3. Thinking recursively

7.3.1. Mundane recursion

7.3.2. Tail calls and recur

7.3.3. Don’t forget your trampoline

7.3.4. Continuation-passing style

7.4. Putting it all together: A* pathfinding

7.4.1. The world

7.4.2. Neighbors

7.4.3. The A* implementation

7.4.4. Notes about the A* implementation

7.5. Summary

Part 4 Large-scale design

8. Macros

8.1. Data is code is data

8.1.1. Syntax-quote, unquote, and splicing

8.1.2. Macro rules of thumb

8.2. Defining control structures

8.2.1. Defining control structures without syntax-quote

8.2.2. Defining control structures using syntax-quote and unquoting

8.3. Macros combining forms

8.4. Using macros to change forms

8.5. Using macros to control symbolic resolution time

8.5.1. Anaphora

8.5.2. (Arguably) useful selective name capturing

8.6. Using macros to manage resources

8.7. Putting it all together: macros returning functions

8.8. Summary

9. Combining data and code

9.1. Namespaces

9.1.1. Creating namespaces

9.1.2. Expose only what’s needed

9.1.3. Declarative inclusions and exclusions

9.2. Exploring Clojure multimethods with the Universal Design Pattern

9.2.1. The parts

9.2.2. Basic use of the Universal Design Pattern

9.2.3. Multimethods to the rescue

9.2.4. Ad hoc hierarchies for inherited behaviors

9.2.5. Resolving conflict in hierarchies

9.2.6. Arbitrary dispatch for true maximum power

9.3. Types, protocols, and records

9.3.1. Records

9.3.2. Protocols

9.3.3. Building from a more primitive base with deftype

9.4. Putting it all together: a fluent builder for chess moves

9.4.1. Java implementation

9.4.2. Clojure implementation

9.5. Summary

10. Mutation and concurrency

10.1. When to use refs

10.1.1. Using refs for a mutable game board

10.1.2. Transactions

10.1.3. Embedded transactions

10.1.4. The things that STM makes easy

10.1.5. Potential downsides

10.1.6. The things that make STM unhappy

10.2. Refactoring with refs

10.2.1. Fixing the game board example

10.2.2. Commutative change with commute

10.2.3. Vulgar change with ref-set

10.2.4. Refs under stress

10.3. When to use agents

10.3.1. In-process vs. distributed concurrency models

10.3.2. Controlling I/O with an agent

10.3.3. The difference between send and send-off

10.3.4. Error handling

10.3.5. When not to use agents

10.4. When to use atoms

10.4.1. Sharing across threads

10.4.2. Using atoms in transactions

10.5. When to use locks

10.5.1. Safe mutation through locking

10.5.2. Using Java’s explicit locks

10.6. Vars and dynamic binding

10.6.1. The binding macro

10.6.2. Creating a named var

10.6.3. Creating anonymous vars

10.6.4. Dynamic scope

10.7. Summary

11. Parallelism

11.1. When to use futures

11.1.1. Futures as callbacks

11.2. When to use promises

11.2.1. Parallel tasks with promises

11.2.2. Callback API to blocking API

11.2.3. Deterministic deadlocks

11.3. Parallel operations

11.3.1. The pvalues macro

11.3.2. The pmap function

11.3.3. The pcalls function

11.4. A brief introduction to reducer/fold

11.5. Summary

Part 5 Host symbiosis


12.1. Generating objects on the fly with proxy

12.1.1. A simple dynamic web service

12.2. Clojure gen-class and GUI programming

12.2.1. Namespaces as class specifications

12.2.2. The guts of namespace compilation

12.2.3. Exploring user interface design and development with Clojure

12.3. Clojure’s relationship to Java arrays

12.3.1. Types of arrays: primitive and reference

12.3.2. Array mutability

12.3.3. Arrays' unfortunate naming convention

12.3.4. Multidimensional arrays

12.3.5. Variadic method/constructor calls

12.4. All Clojure functions implement …​

12.4.1. The java.util.Comparator interface

12.4.2. The java.lang.Runnable interface

12.4.3. The java.util.concurrent.Callable interface

12.5. Using Clojure data structures in Java APIs

12.5.1. The java.util.List interface

12.5.2. The java.lang.Comparable interface

12.5.3. The java.util.RandomAccess interface

12.5.4. The java.util.Collection interface

12.5.5. The java.util.Set interface

12.6. The definterface macro

12.6.1. Generating interfaces on the fly

12.7. Be wary of exceptions

12.7.1. A bit of background regarding exceptions

12.7.2. Runtime vs. compile-time exceptions

12.7.3. Handling exceptions

12.7.4. Custom exceptions

12.8. Summary

13. Why ClojureScript?

13.1. Implementation vs. interface

13.2. Compiler internals: analysis vs. emission

13.2.1. Stages of compilation

13.2.2. Web Audio

13.2.3. Advanced compilation

13.2.4. Generating an externs.js file

13.3. Compile vs. run

13.4. Summary

Part 6 Tangential considerations

14. Data-oriented programming

14.1. Code as code, and data as data

14.1.1. A strict line betwixt

14.1.2. ORMG

14.1.3. Common ways to derive information from data

14.1.4. PLOP

14.2. Data as data

14.2.1. The benefits of value

14.2.2. Tagged literals

14.3. Data as code

14.3.1. The data-programmable engine

14.3.2. Examples of data-programmable engines

14.3.3. Case study: simple event sourcing

14.4. Code as data as code

14.4.1. Hart’s discovery and homoiconicity

14.4.2. Clojure code is data

14.4.3. Putting parentheses around the specification

14.5. Summary

15. Performance

15.1. Type hints

15.1.1. Advantages of type adornment

15.1.2. Type-hinting arguments and returns

15.1.3. Type-hinting objects

15.2. Transients

15.2.1. Ephemeral garbage

15.2.2. Transients compare in efficiency to mutable collections

15.3. Chunked sequences

15.3.1. Regaining one-at-a-time laziness

15.4. Memoization

15.4.1. Reexamining memoization

15.4.2. A memoization protocol

15.4.3. Abstraction-oriented programming

15.5. Understanding coercion

15.5.1. Using primitive longs

15.5.2. Using primitive doubles

15.5.3. Using auto-promotion

15.6. Reducibles

15.6.1. An example reducible collection

15.6.2. Deriving your first reducing function transformer

15.6.3. More reducing function transformers

15.6.4. Reducible transformers

15.6.5. Performance of reducibles

15.6.6. Drawbacks of reducibles

15.6.7. Integrating reducibles with Clojure reduce

15.6.8. The fold function: reducing in parallel

15.7. Summary

16. Thinking programs

16.1.1. A brute-force Sudoku solver

16.1.2. Declarative is the goal

16.2. Thinking data via unification

16.2.1. Potential equality, or satisfiability

16.2.2. Substitution

16.2.3. Unification

16.3. An introduction to core.logic

16.3.1. It’s all about unification

16.3.2. Relations

16.3.3. Subgoals

16.4. Constraints

16.4.1. An introduction to constraint programming

16.4.2. Limiting binding via finite domains

16.4.3. Solving Sudoku with finite domains

16.5. Summary

17. Clojure changes the way you think

17.1. Thinking in the domain

17.1.1. A ubiquitous DSL

17.1.2. Implementing a SQL-like DSL to generate queries

17.1.3. A note about Clojure’s approach to DSLs

17.2. Testing

17.2.1. Some useful unit-testing techniques

17.2.2. Contracts programming

17.3. Invisible design patterns

17.3.1. Clojure’s first-class design patterns

17.4. Error handling and debugging

17.4.1. Error handling

17.4.2. Debugging

17.5. Fare thee well



About the Technology

The Clojure programming language is a dialect of Lisp that runs on the Java Virtual Machine and JavaScript runtimes. It is a functional programming language that offers great performance, expressive power, and stability by design. It gives you built-in concurrency and the predictable precision of immutable and persistent data structures. And it's really, really fast. The instant you see long blocks of Java or Ruby dissolve into a few lines of Clojure, you'll know why the authors of this book call it a "joyful language." It's no wonder that enterprises like Staples are betting their infrastructure on Clojure.

About the book

The Joy of Clojure, Second Edition is a deep account of the Clojure language. Fully updated for Clojure 1.6, this new edition goes beyond the syntax to show you how to write fluent Clojure code. You'll learn functional and declarative approaches to programming and will master techniques that make Clojure elegant and efficient. The book shows you how to solve hard problems related to concurrency, interoperability, and performance, and how great it can be to think in the Clojure way.

What's inside

  • Build web apps using ClojureScript
  • Master functional programming techniques
  • Simplify concurrency
  • Covers Clojure 1.6

About the author

Michael Fogus and Chris Houser are contributors to the Clojure and ClojureScript programming languages and the authors of various Clojure libraries and language features.

combo $49.99 pBook + eBook
eBook $39.99 pdf + ePub + kindle

FREE domestic shipping on three or more pBooks

Clojure changes the way we think about programming—this book changes the way we think about Clojure.

Cristofer Weber, NeoGrid

Clear examples for both novice and experienced programmers.

Jasper Lievisse Adriaanse, M:Tier

A joy to read. I couldn't put it down.

Heather Campbell, Kainos