This article is part of a series describing a port of the samples from Practical Common Lisp (PCL) to Clojure. You will probably want to read the intro first.
This article covers Chapter 7, Macros: Standard Control Constructs.
Common Lisp control constructs are generally part of the standard library, not the core language. Ditto for Clojure. If you don't find a control construct you want, you can always roll it yourself. For example, Clojure doesn't have an unless
, so here goes:
defmacro
differs from Common Lisp in two important ways.
[...]
, not a list (...)
. (This is true for functions as well, I just hadn't mentioned it yet). Clojure gives vectors, sets, and maps equal billing with lists by giving them their own literal syntax.,
and ,@
, Clojure uses ~
, and ~@
.The avoidance of commas in read macros is a well-considered decision. Commas are whitespace in Clojure. This often results in an interface that is simultaneously human-friendly and list-y. The following two expressions are equivalent:
The latter form makes the map more readable, and more similar to other languages.
Common Lisp provides dolist
for iterating a list. Clojure works in terms of sequences, which are collections that can be traversed in a list-like way. The Clojure analog to dolist
is doseq
. It can work with lists:
doseq
also works with maps. Note the destructuring bind since I care only about the values:
In fact, doseq
works with any kind of sequence (hence the name). (iterate inc 1)
produces an infinite collection incrementing up from 1. (take 5 ...)
pulls a finite set of 5 elements from a collection.
Don't try to doseq
an infinite collection, and don't say I didn't warn you.
Common Lisp provides dotimes
for iteration with counting. Here is the Clojure version of PCL's multiplication table example:
Common Lisp provides some more general control constructs: do
and loop
. Clojure's functions of the same name serve very different purposes. Clojure's do
is equivalent to CL's progn
, and Clojure's loop
works with recur
.
You could write Clojure macros to emulate CL's do
and loop
, but you probably won't want too. Instead, you can use list comprehensions or lazy sequences, which I will introduce later in this series.
Like CL, Clojure defines control structures using macros. Also like CL, Clojure has control structures that are functional, plus some that are evaluated for their side effects. Clojure's control structures tend to use fewer parentheses.
Clojure does not duplicate CL's general purpose imperative control structures. Instead, you can often use list comprehensions and lazy sequences.
The sample code is available at http://github.com/stuarthalloway/practical-cl-clojure.