mbe.lsp defines for Common Lisp the macro definers
letrec-syntax, as described in the
Scheme report R5RS .
These macro definers, also called macro by example (MBE), use simple patterns, including ellipsis, to specify how a macro should be expanded. They were propounded by Eugene Kohlbecker [1, 2] in the mid-1980s and became part of the Scheme standard in 1991.
MBE contrast dramatically with Common Lisp's
macrolet, which require the user to program expansions
using extensive list destructuring and restructuring, often
involving several nestings of backquotes, quotes, unquotes
and spliced unquotes.
For example, a
let macro could be defined using MBE
as follows (we will use
my-let in order not to clash
with the standard
(define-syntax my-let (syntax-rules () ((my-let ((x v) ***) e ***) ((lambda (x ***) e ***) v ***))))
*** indicates an ellipsis. Thus, the input pattern
***) matches a list, each of whose elements match
v). When expanding, the output pattern
represents a list containing all the
x's in the same
order that they matched the
x's in the input
defmacro, the definition would be:
(defmacro my-let (xvxv &rest ee) `((lambda ,(mapcar #'car xvxv) ,@ee) ,@(mapcar #'cadr xvxv)))
Global MBE macros are defined using the form
define-syntax. Its first subexpression is the name of the
macro to be defined, and its second subexpression is the
syntax-rules governing that macro's expansion.
The first subexpression of
syntax-rules is a list of
auxiliary keywords pertaining to the macro. The
remaining subexpressions of
syntax-rules are the macro
expansion clauses. Each clause consists of an in-pattern and an out-pattern. If the macro call
matches an in-pattern, it expands to the corresponding
out-pattern. The clauses are tried in sequence: a macro
call expands based on the first in-pattern that it matches.
Here is an example of a disjunction form
my-or (same as
(define-syntax my-or (syntax-rules () ((my-or) nil) ((my-or arg1) arg1) ((my-or arg1 arg2) (let ((temp arg1)) (if temp temp arg2))) ((my-or arg1 arg2 arg3 ***) (my-or (my-or arg1 arg2) arg3 ***))))
There is one problem with the definition of
If you have a global variable
temp bound to
then the following expression
(my-or nil temp)
does not give the expected result (
t). This is because
(my-or nil temp)
(let ((temp nil)) (if temp temp temp))
which evaluates to
nil. The temporary lexical variable
temp introduced in the macro expansion shadows (or captures) the
temp and therefore causes an erroneous result.
The Scheme report requires that the macro definers be hygienic, i.e., that they automatically avoid these lexical captures. This Common Lisp implementation does not provide hygiene. There are two ways out:
1. Use unusual names for any lexical variables you introduce in the expansion pattern, e.g.,
... ((my-or arg1 arg2) (let ((__#temp#__ arg1)) (if __#temp#__ __#temp#__ arg2))) ...
and then hope that nobody ever uses
2. A better alternative is to use a generated symbol that
is guaranteed not to clash with anything Lisp or you can
come up with before or after. To introduce this gensym,
mbe.lsp provides a
with-wrapper for the expansion
... ((my-or arg1 arg2) (with ((temp (gensym))) (let ((temp arg1)) (if temp temp arg2)))) ...
temp identifier used in the expansion pattern
will be a gensym, and not the symbol literally named
define-syntax defines globally visible macros. For
local macros (cf. Common Lisp's
(let-syntax ((either (syntax-rules () ((either x y) (with ((tmp (gensym))) (let ((tmp x)) (if tmp tmp y))))))) (either "this" "that"))
either macro is local to the
and will not be visible outside.
The Scheme report distinguishes between
letrec-syntax the same way that
Common Lisp distinguishes between
However, this distinction is not well preserved in
because these forms are defined using
macrolet doesn't have the same scoping style.
Eugene E. Kohlbecker, Jr., Daniel P. Friedman, Matthias Felleisen, and Bruce Duba, ``Hygienic Macro Expansion'', in Proc. 1986 ACM Conf. on Lisp and Functional Programming, pp. 151-161.
Eugene E. Kohlbecker, Jr., Syntactic Extensions in the Programming Language Lisp, PhD thesis, Indiana Univ., 1986.
Revised (5) Report on the Algorithmic Language Scheme (``R5RS''), eds. Richard Kelsey, William Clinger, and Jonathan Rees, 1998.
1 The ellipsis is represented by `
in Scheme. This Common Lisp implementation uses
***' instead, since `
...' is disallowed by Common
Lisp's identifier conventions.