Macros transform code, and code is data.
Let's see how we transform code by hand and how macros can help simplify that.
Example
Here is a list you want to transform:
(when2 test 1 2 3 4)
It is a list of 6 atomic values, a mix of symbols and integers.
Let's call this list input.
Here, we want to produce this code:
(if test (progn 1 2 3 4) nil)
Extracting values
From input:
- You can access
test by taking its second value.
- You can access
(1 2 3 4) by taking the cdr twice (i.e. cddr)
Building code
Knowing this, you can build the resulting list:
(let ((input '(when2 test 1 2 3 4)))
(list 'if
(second input)
(list* 'progn (cddr input))
nil))
=> (if test (progn 1 2 3 4) nil)
I used LIST* to build a list which starts with PROGN and is followed by another list.
General solution
In the above example, you can see that we don't rely much on the exact content of our input list, but only its general shape.
In fact, we can make input a parameter, and we obtain the transformation function we want:
(lambda (input)
(list 'if
(second input)
(list* 'progn (cddr input))
nil))
Backquote
The backquote syntax is a shorter way of writing the above; the
backquoted code is not evaluated and acts as a template where you
place expressions that are evaluated (a little bit like string
interpolation, except saner). You switch from quoted to evaluated forms:
`(a ,b) is equivalent to (list 'a b)
`(a b ,@list e f), with list the list (c d) gives (a b c d e f), placing all elements in list at the same level as the ones surrounding it. This is the splice operator.
Instead of the above function, we could write:
(lambda (input)
`(if ,(second input) (progn ,@(cddr input)) nil))
Destructuring code
Macros further simplify writing those code transformation functions by providing powerful Macro Lambda Lists.
Instead of explicitly calling (second input) or (cddr input), you use pattern matching;
you declare what shape the input code should have, and the macro binds variables to each subpart of the input, while transforming code:
(defmacro when2 (test &body expressions)
`(if ,test (progn ,@expressions) nil))
Here, the name of the macro, its argument and the body of code that is included in it are easily declared and used in the expansion code. See DEFMACRO.
The effect is the same as building code from lists of atoms by hand, but both the pattern-matching syntax and the quasi-quotation syntax make writing macros easier.