This article covers Chapter 8, Macros: Defining Your Own.
Rolling your own
Lisp macros gain their power by controlling argument evaluation. In a normal Lisp function all arguments are evaluated when calling a function. Consider this call to function
(foo a b)
b are evaluated, and then passed to function
foo were a macro, however, all bets would be off. Then
foo's arguments might be evaluated in bizarre orders, or not at all.
This may seem a little crazy until you consider a simple
(if monday (wake-up) (sleep))
if cannot possibly be a normal Lisp function. If it were, you would always both
sleep, regardless of the value of
if example suggests, control flow is an obvious use case for macros. PCL demonstrates custom macros by defining a new control flow macro named
Preparing for do-primes
In order to implement
do-primes, I will need a primeness test. For clarity, I will divide this into two functions. First, a simple helper to detect factors.
(defn divides? [candidate-divisor dividend] (zero? (rem dividend candidate-divisor)))
Now I can tell when one number divides another:
user=> (divides? 7 42) true user=> (divides? 11 42) false
A prime is simply a number with no divisors greater than one. I am a busy guy, so I won't check all the natural numbers, only those from two up to the square root of the number being tested. Here is a simple primeness test:
; yes, I know there are faster ways. (defn prime? [num] (when (> num 1) (every? (fn [x] (not (divides? x num))) (range 2 (inc (int (Math/sqrt num)))))))
Sequences of primes
My eventual objective is to call
do-primes like this:
(do-primes i 100 200 (print (format "%d " i)))
i is the loop variable and runs the primes from
200. Because Clojure has nice support for infinite sequences, I find it easier to begin by thinking in terms of the pure math. So, here is a function that returns the sequence of primes starting from a number:
(defn primes-from [number] (filter prime? (iterate inc number)))
(iterate inc number) returns an infinite sequence starting with
number and then incrementing by one for each subsequent element. The
filter then whittles this down to numbers that are prime.
This sequence is infinite, so don't try to view it from the console. Take your primes a few at the time:
user=> (take 5 (primes-from 1000)) (1009 1013 1019 1021 1031)
Now I need a simple helper that begins with
primes-from, but cuts off the sequence at a chosen
(defn primes-in-range [start end] (for [x (primes-from start) :while (<= x end)] x))
for is a list comprehension. It takes all the
(primes-from start), but only
while those numbers are still less than or equal to
Now I am finally ready to write the macro
(defmacro do-primes [var start end & body] `(doseq [~var (primes-in-range ~start ~end)] ~@body))
Macros work in two steps: expansion followed by normal Lisp evaluation. The expansion phase is like a template substitution, but with the full power of Lisp at your disposal.
In the definition of
do-primes above, the syntax-quote (
\`) identifies the static part of the template:
- For symbols, syntax-quote resolves the name to a fully qualified symbol (with some exceptions we don't need to worry about in this example).
- For lists, syntax-quote will recursively syntax-quote the contained forms.
The unquote (
~) and splicing-unquote (
~@) provide the dynamic part of the template by exempting their forms from syntax quoting rules.
Your reaction at this point should be "That's a lot of ugly punctuation." Fear not,
macroexpand-1 will ease the pain.
macroexpand-1 will show you how Clojure expands the macro, without executing the expanded result. This gives you a chance to experiment with the rules for quoting and unquoting. Here is an example:
user=> (macroexpand-1 '(do-primes i 1 10 (print i))) (clojure/doseq i (pcl.chap_08/primes-in-range 1 10) (print i))
Looking back at the definition of
do-primes, here is what happened:
doseqexpanded to the fully-qualified
clojure/doseq. (I haven't covered namespaces yet, but the
clojurenamespace contains most of the Clojure core.)
10are direct expansions from the macro call.
primes-in-rangeis one of the helper functions I wrote earlier. In the sample repository, I have placed this in the
pcl/chap_08namespace, hence the expansion.
bodycontains a list of things I want to do with my primes, specifically
((print i)). That is almost what I need, except a few too many parens. The "splice" part of splicing unquote gets rid of the extra parens, splicing the list into the template. This is exactly what I need to match the
Now I can
user=> (do-primes i 100 150 (print (format "%d " i))) 101 103 107 109 113 127 131 137 139 149
The easiest way to write a macro is to work backwards. Write the form that you want the macro to expand into, and then test interactively with
macroexpand-1 until you have a macro that expends correctly.
Macros are hard, and I have skipped some of the building blocks here. Check out the chapter in PCL.
The sample code is available at http://github.com/stuarthalloway/practical-cl-clojure.
- 2008/09/23: initial version
- 2008/11/24: Updated to the new uniform binding syntax