This article covers Chapter 8, Macros: Defining 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
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 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
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.
Now I can tell when one number divides another:
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:
My eventual objective is to call
do-primes like this:
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:
(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:
Now I need a simple helper that begins with
primes-from, but cuts off the sequence at a chosen
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
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:
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:
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
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.