represented as S-expressions, then the interpreter for that language cannot be written in that language, but in another meta-language which does deal with S- expressions. Macros, which transform one S-expression (representing a macro call) to another (the replacement form, or the interpretation of the call), clearly should be expressed in this meta-language also. The fact that in most LISP systems the language and the meta-language appear to coincide is a source of both power and confusion.
In the PDP-10 MacLISP implementation of SCHEME, four separate macro
mechanisms are used in practice. One is the MacLISP read-macro mechanism [Moon],
which performs transformations such as 'FOO => (QUOTE FOO) when an expression is
read from a file. The other three are as described earlier, processed by the
interpreter or compiler, and differ only in that one kind is recognized by the
MacLISP interpreter as well while the other two are used only by SCHEME, and that
of the latter two one kind is written in MacLISP and the other kind in SCHEME
itself.
There is a growing library of SCHEME macros which express a variety of traditional programming constructs in terms of other, more primitive constructs, and eventually in terms of the small set of primitives. A number of these are catalogued in [Imperative] and [Revised Report]. Others were invented in the course of writing RABBIT. We shall give some examples here.
The BLOCK macro is similar to the MacLISP PROGN; it evaluates all its arguments and returns the value of the last one. One critical characteristic is that the last argument is evaluated "tail-recursively" (I use horror quotes because normally we speak of invocation, not evaluation, as being tail-recursive). An expansion rule is given for this in [Imperative] equivalent to:
(BLOCK x) => x (BLOCK x . rest) => ((LAMBDA (DUMMY) (BLOCK . rest)) x)