#
Chapter 8
Macros

Users can create their own special forms by defining macros. A macro is a symbol that has a transformer procedure associated with it. When Scheme encounters a macro-expression -- ie, a form whose head is a macro -- , it applies the macro's transformer to the subforms in the macro-expression, and evaluates the result of the transformation.

Ideally, a macro specifies a purely textual transformation from code text to other code text. This kind of transformation is useful for abbreviating an involved and perhaps frequently occurring textual pattern.

A macro is defined using the special form `define-macro` (but see sec A.3).3 For example, if your Scheme lacks the conditional special form `when`, you could define `when` as the following macro:

    (define-macro when
      (lambda (test . branch)
        (list 'if test
          (cons 'begin branch))))

This defines a `when`-transformer that would convert a `when`-expression into the equivalent `if`-expression. With this macro definition in place, the `when`-expression

    (when (< (pressure tube) 60)
       (open-valve tube)
       (attach floor-pump tube)
       (depress floor-pump 5)
       (detach floor-pump tube)
       (close-valve tube))

will be converted to another expression, the result of applying the `when`-transformer to the `when`-expression's subforms:

    (apply
      (lambda (test . branch)
        (list 'if test
          (cons 'begin branch)))
      '((< (pressure tube) 60)
          (open-valve tube)
          (attach floor-pump tube)
          (depress floor-pump 5)
          (detach floor-pump tube)
          (close-valve tube)))

The transformation yields the list

    (if (< (pressure tube) 60)
        (begin
          (open-valve tube)
          (attach floor-pump tube)
          (depress floor-pump 5)
          (detach floor-pump tube)
          (close-valve tube)))

Scheme will then evaluate this expression, as it would any other.

As an additional example, here is the macro-definition for `when`'s counterpart `unless`:

    (define-macro unless
      (lambda (test . branch)
        (list 'if
              (list 'not test)
              (cons 'begin branch))))

Alternatively, we could invoke `when` inside `unless`'s definition:

    (define-macro unless
      (lambda (test . branch)
        (cons 'when
              (cons (list 'not test) branch))))

Macro expansions can refer to other macros.

## 8.1 Specifying the expansion as a template

A macro transformer takes some s-expressions and produces an s-expression that will be used as a form. Typically this output is a list. In our `when` example, the output list is created using

    (list 'if test
      (cons 'begin branch))

where `test` is bound to the macro's first subform, ie,

    (< (pressure tube) 60)

and `branch` to the rest of the macro's subforms, ie,

    ((open-valve tube)
     (attach floor-pump tube)
     (depress floor-pump 5)
     (detach floor-pump tube)
     (close-valve tube))

Output lists can be quite complicated. It is easy to see that a more ambitious macro than `when` could lead to quite an elaborate construction process for the output list. In such cases, it is more convenient to specify the macro's output form as a template, with the macro arguments inserted at appropriate places to fill out the template for each particular use of the macro. Scheme provides the backquote syntax to specify such templates. Thus the expression

    (list 'IF test
      (cons 'BEGIN branch))

is more conveniently written as

    `(IF ,test
      (BEGIN ,@branch))

We can refashion the `when` macro-definition as:

    (define-macro when
      (lambda (test . branch)
        `(IF ,test
             (BEGIN ,@branch))))

Note that the template format, unlike the earlier list construction, gives immediate visual indication of the shape of the output list. The backquote (```) introduces a template for a list. The elements of the template appear verbatim in the resulting list, except when they are prefixed by a comma (``,`') or a comma-splice (``,@`'). (For the purpose of illustration, we have written the verbatim elements of the template in UPPER-CASE.)

The comma and the comma-splice are used to insert the macro arguments into the template. The comma inserts the result of evaluating its following expression. The comma-splice inserts the result of evaluating its following expression after splicing it, ie, it removes the outermost set of parentheses. (This implies that an expression introduced by comma-splice must be a list.)

In our example, given the values that `test` and `branch` are bound to, it is easy to see that the template will expand to the required

    (IF (< (pressure tube) 60)
        (BEGIN
          (open-valve tube)
          (attach floor-pump tube)
          (depress floor-pump 5)
          (detach floor-pump tube)
          (close-valve tube)))

## 8.2 Avoiding variable capture inside macros

A two-argument disjunction form, `my-or`, could be defined as follows:

    (define-macro my-or
      (lambda (x y)
        `(if ,x ,x ,y)))

`my-or` takes two arguments and returns the value of the first of them that is true (ie, non-`#f`). In particular, the second argument is evaluated only if the first turns out to be false.

    (my-or 1 2)
    =>  1

    (my-or #f 2)
    =>  2

There is a problem with the `my-or` macro as it is written. It re-evaluates the first argument if it is true: once in the `if`-test, and once again in the ``then'' branch. This can cause undesired behavior if the first argument were to contain side-effects, eg,

    (my-or
      (begin
        (display "doing first argument")
         (newline)
         #t)
      2)

displays `"doing first argument"` twice.

This can be avoided by storing the `if`-test result in a local variable:

    (define-macro my-or
      (lambda (x y)
        `(let ((temp ,x))
           (if temp temp ,y))))

This is almost OK, except in the case where the second argument happens to contain the same identifier `temp` as used in the macro definition. Eg,

    (define temp 3)

    (my-or #f temp)
    =>  #f

Surely it should be 3! The fiasco happens because the macro uses a local variable `temp` to store the value of the first argument (`#f`) and the variable `temp` in the second argument got captured by the `temp` introduced by the macro.

To avoid this, we need to be careful in choosing local variables inside macro definitions. We could choose outlandish names for such variables and hope fervently that nobody else comes up with them. Eg,

    (define-macro my-or
      (lambda (x y)
        `(let ((+temp ,x))
           (if +temp +temp ,y))))

This will work given the tacit understanding that `+temp` will not be used by code outside the macro. This is of course an understanding waiting to be disillusioned.

A more reliable, if verbose, approach is to use generated symbols that are guaranteed not to be obtainable by other means. The procedure `gensym` generates unique symbols each time it is called. Here is a safe definition for `my-or` using `gensym`:

    (define-macro my-or
      (lambda (x y)
        (let ((temp (gensym)))
          `(let ((,temp ,x))
             (if ,temp ,temp ,y)))))

In the macros defined in this document, in order to be concise, we will not use the `gensym` approach. Instead, we will consider the point about variable capture as having been made, and go ahead with the less cluttered `+`-as-prefix approach. We will leave it to the astute reader to remember to convert these `+`-identifiers into gensyms in the manner outlined above.

## 8.3 `fluid-let`

Here is a definition of a rather more complicated macro, `fluid-let` (sec 5.2). `fluid-let` specifies temporary bindings for a set of already existing lexical variables. Given a `fluid-let` expression such as

    (fluid-let ((x 9) (y (+ y 1)))
      (+ x y))

we want the expansion to be

    (let ((OLD-X x) (OLD-Y y))
      (set! x 9)
      (set! y (+ y 1))
      (let ((RESULT (begin (+ x y))))
        (set! x OLD-X)
        (set! y OLD-Y)
        RESULT))

where we want the identifiers `OLD-X`, `OLD-Y`, and `RESULT` to be symbols that will not capture variables in the expressions in the `fluid-let` form.

Here is how we go about fashioning a `fluid-let` macro that implements what we want:

    (define-macro fluid-let
      (lambda (xexe . body)
        (let ((xx (map car xexe))
              (ee (map cadr xexe))
              (old-xx (map (lambda (ig) (gensym)) xexe))
              (result (gensym)))
          `(let ,(map (lambda (old-x x) `(,old-x ,x))
                      old-xx xx)
             ,@(map (lambda (x e)
                      `(set! ,x ,e))
                    xx ee)
             (let ((,result (begin ,@body)))
               ,@(map (lambda (x old-x)
                        `(set! ,x ,old-x))
                      xx old-xx)
               ,result)))))

The macro's arguments are: `xexe`, the list of variable/expression pairs introduced by the `fluid-let`; and `body`, the list of expressions in the body of the `fluid-let`. In our example, these are `((x 9) (y (+ y 1)))` and `((+ x y))` respectively.

The macro body introduces a bunch of local variables: `xx` is the list of the variables extracted from the variable/expression pairs. `ee` is the corresponding list of expressions. `old-xx` is a list of fresh identifiers, one for each variable in `xx`. These are used to store the incoming values of the `xx`, so we can revert the `xx` back to them once the `fluid-let` body has been evaluated. `result` is another fresh identifier, used to store the value of the `fluid-let` body. In our example, `xx` is `(x y)` and `ee` is `(9 (+ y 1))`. Depending on how your system implements `gensym`, `old-xx` might be the list `(GEN-63 GEN-64)`, and `result` might be `GEN-65`.

The output list is created by the macro for our given example looks like

    (let ((GEN-63 x) (GEN-64 y))
      (set! x 9)
      (set! y (+ y 1))
      (let ((GEN-65 (begin (+ x y))))
        (set! x GEN-63)
        (set! y GEN-64)
        GEN-65))

which matches our requirement.

3 MzScheme provides `define-macro` via the `defmacro` library. Use `(require (lib "defmacro.ss"))` to load this library.