Syntax Rules (Guile Reference Manual)
Next:
Support for the
syntax-case
System
, Previous:
Defining Macros
, Up:
Macros
Contents
][
Index
6.8.2 Syntax-rules Macros
syntax-rules
macros are simple, pattern-driven syntax transformers, with
a beauty worthy of Scheme.
Syntax:
syntax-rules
literals (pattern template) …
Create a syntax transformer that will rewrite an expression using the rules
embodied in the
pattern
and
template
clauses.
syntax-rules
macro consists of three parts: the literals (if any), the
patterns, and as many templates as there are patterns.
When the syntax expander sees the invocation of a
syntax-rules
macro, it
matches the expression against the patterns, in order, and rewrites the
expression using the template from the first matching pattern. If no pattern
matches, a syntax error is signaled.
Patterns
Hygiene
Shorthands
Reporting Syntax Errors in Macros
Specifying a Custom Ellipsis Identifier
Further Information
6.8.2.1 Patterns
We have already seen some examples of patterns in the previous section:
(unless condition exp ...)
(my-or exp)
, and so on. A pattern is
structured like the expression that it is to match. It can have nested structure
as well, like
(let ((var val) ...) exp exp* ...)
. Broadly speaking,
patterns are made of lists, improper lists, vectors, identifiers, and datums.
Users can match a sequence of patterns using the ellipsis (
...
).
Identifiers in a pattern are called
literals
if they are present in the
syntax-rules
literals list, and
pattern variables
otherwise. When
building up the macro output, the expander replaces instances of a pattern
variable in the template with the matched subexpression.
(define-syntax kwote
(syntax-rules ()
((kwote exp)
(quote exp))))
(kwote (foo . bar))
⇒ (foo . bar)
An improper list of patterns matches as rest arguments do:
(define-syntax let1
(syntax-rules ()
((_ (var val) . exps)
(let ((var val)) . exps))))
However this definition of
let1
probably isn’t what you want, as the tail
pattern
exps
will match non-lists, like
(let1 (foo 'bar) . baz)
. So
often instead of using improper lists as patterns, ellipsized patterns are
better. Instances of a pattern variable in the template must be followed by an
ellipsis.
(define-syntax let1
(syntax-rules ()
((_ (var val) exp ...)
(let ((var val)) exp ...))))
This
let1
probably still doesn’t do what we want, because the body
matches sequences of zero expressions, like
(let1 (foo 'bar))
. In this
case we need to assert we have at least one body expression. A common idiom for
this is to name the ellipsized pattern variable with an asterisk:
(define-syntax let1
(syntax-rules ()
((_ (var val) exp exp* ...)
(let ((var val)) exp exp* ...))))
A vector of patterns matches a vector whose contents match the patterns,
including ellipsizing and tail patterns.
(define-syntax letv
(syntax-rules ()
((_ #((var val) ...) exp exp* ...)
(let ((var val) ...) exp exp* ...))))
(letv #((foo 'bar)) foo)
⇒ bar
Literals are used to match specific datums in an expression, like the use of
=>
and
else
in
cond
expressions.
(define-syntax cond1
(syntax-rules (=> else)
((cond1 test => fun)
(let ((exp test))
(if exp (fun exp) #f)))
((cond1 else exp exp* ...)
(begin exp exp* ...))
((cond1 test exp exp* ...)
(if test (begin exp exp* ...)))))
(define (square x) (* x x))
(cond1 10 => square)
⇒ 100
(let ((=> #t))
(cond1 10 => square))
⇒ #
A literal matches an input expression if the input expression is an identifier
with the same name as the literal, and both are unbound
15
Although literals can be unbound, usually they are bound to allow them
to be imported, exported, and renamed. See
Modules
, for more
information on imports and exports. In Guile there are a few standard
auxiliary syntax definitions, as specified by R6RS and R7RS:
Scheme Syntax:
else
Scheme Syntax:
=>
Scheme Syntax:
Scheme Syntax:
...
Auxiliary syntax definitions.
These are defined with a macro that never matches, e.g.:
(define-syntax else (syntax-rules ()))
If a pattern is not a list, vector, or an identifier, it matches as a literal,
with
equal?
(define-syntax define-matcher-macro
(syntax-rules ()
((_ name lit)
(define-syntax name
(syntax-rules ()
((_ lit) #t)
((_ else) #f))))))
(define-matcher-macro is-literal-foo? "foo")
(is-literal-foo? "foo")
⇒ #t
(is-literal-foo? "bar")
⇒ #f
(let ((foo "foo"))
(is-literal-foo? foo))
⇒ #f
The last example indicates that matching happens at expansion-time, not
at run-time.
Syntax-rules macros are always used as
macro
args
, and
the
macro
will always be a symbol. Correspondingly, a
syntax-rules
pattern must be a list (proper or improper), and the first pattern in that list
must be an identifier. Incidentally it can be any identifier – it doesn’t have
to actually be the name of the macro. Thus the following three are equivalent:
(define-syntax when
(syntax-rules ()
((when c e ...)
(if c (begin e ...)))))
(define-syntax when
(syntax-rules ()
((_ c e ...)
(if c (begin e ...)))))
(define-syntax when
(syntax-rules ()
((something-else-entirely c e ...)
(if c (begin e ...)))))
For clarity, use one of the first two variants. Also note that since the pattern
variable will always match the macro itself (e.g.,
cond1
), it is actually
left unbound in the template.
6.8.2.2 Hygiene
syntax-rules
macros have a magical property: they preserve referential
transparency. When you read a macro definition, any free bindings in that macro
are resolved relative to the macro definition; and when you read a macro
instantiation, all free bindings in that expression are resolved relative to the
expression.
This property is sometimes known as
hygiene
, and it does aid in code
cleanliness. In your macro definitions, you can feel free to introduce temporary
variables, without worrying about inadvertently introducing bindings into the
macro expansion.
Consider the definition of
my-or
from the previous section:
(define-syntax my-or
(syntax-rules ()
((my-or)
#t)
((my-or exp)
exp)
((my-or exp rest ...)
(let ((t exp))
(if t
(my-or rest ...))))))
A naive expansion of
(let ((t #t)) (my-or #f t))
would yield:
(let ((t #t))
(let ((t #f))
(if t t t)))
⇒ #f
Which clearly is not what we want. Somehow the
in the definition is
distinct from the
at the site of use; and it is indeed this distinction
that is maintained by the syntax expander, when expanding hygienic macros.
This discussion is mostly relevant in the context of traditional Lisp macros
(see
Lisp-style Macro Definitions
), which do not preserve referential transparency. Hygiene
adds to the expressive power of Scheme.
6.8.2.3 Shorthands
One often ends up writing simple one-clause
syntax-rules
macros.
There is a convenient shorthand for this idiom, in the form of
define-syntax-rule
Syntax:
define-syntax-rule
(keyword . pattern) [docstring] template
Define
keyword
as a new
syntax-rules
macro with one clause.
Cast into this form, our
when
example is significantly shorter:
(define-syntax-rule (when c e ...)
(if c (begin e ...)))
6.8.2.4 Reporting Syntax Errors in Macros
Syntax:
syntax-error
message [arg ...]
Report an error at macro-expansion time.
message
must be a string
literal, and the optional
arg
operands can be arbitrary expressions
providing additional information.
syntax-error
is intended to be used within
syntax-rules
templates. For example:
(define-syntax simple-let
(syntax-rules ()
((_ (head ... ((x . y) val) . tail)
body1 body2 ...)
(syntax-error
"expected an identifier but got"
(x . y)))
((_ ((name val) ...) body1 body2 ...)
((lambda (name ...) body1 body2 ...)
val ...))))
6.8.2.5 Specifying a Custom Ellipsis Identifier
When writing macros that generate macro definitions, it is convenient to
use a different ellipsis identifier at each level. Guile allows the
desired ellipsis identifier to be specified as the first operand to
syntax-rules
, as specified by SRFI-46 and R7RS. For example:
(define-syntax define-quotation-macros
(syntax-rules ()
((_ (macro-name head-symbol) ...)
(begin (define-syntax macro-name
(syntax-rules ::: ()
((_ x :::)
(quote (head-symbol x :::)))))
...))))
(define-quotation-macros (quote-a a) (quote-b b) (quote-c c))
(quote-a 1 2 3) ⇒ (a 1 2 3)
6.8.2.6 Further Information
For a formal definition of
syntax-rules
and its pattern language, see
See
Macros
in
Revised(5) Report on the Algorithmic Language
Scheme
syntax-rules
macros are simple and clean, but do they have limitations.
They do not lend themselves to expressive error messages: patterns either match
or they don’t. Their ability to generate code is limited to template-driven
expansion; often one needs to define a number of helper macros to get real work
done. Sometimes one wants to introduce a binding into the lexical context of the
generated code; this is impossible with
syntax-rules
. Relatedly, they
cannot programmatically generate identifiers.
The solution to all of these problems is to use
syntax-case
if you need
its features. But if for some reason you’re stuck with
syntax-rules
, you
might enjoy Joe Marshall’s
syntax-rules
Primer for the Merely Eccentric
16
Footnotes
(15)
Language
lawyers probably see the need here for use of
literal-identifier=?
rather
than
free-identifier=?
, and would probably be correct. Patches
accepted.
(16)
Archived from
the original
on
2013-05-03.
Next:
Support for the
syntax-case
System
, Previous:
Defining Macros
, Up:
Macros
Contents
][
Index