16.1 Pattern-Based Macros
The Racket Guide
Welcome to Racket
Racket Essentials
Built-
In Datatypes
Expressions and Definitions
Programmer-
Defined Datatypes
Modules
Contracts
Input and Output
Regular Expressions
10
Exceptions and Control
11
Iterations and Comprehensions
12
Pattern Matching
13
Classes and Objects
14
Units
15
Reflection and Dynamic Evaluation
16
Macros
17
Creating Languages
18
Concurrency and Synchronization
19
Performance
20
Parallelism
21
Running and Creating Executables
22
More Libraries
23
Dialects of Racket and Scheme
24
Command-
Line Tools and Your Editor of Choice
Bibliography
Index
16
Macros
16.1
Pattern-
Based Macros
16.2
General Macro Transformers
16.3
Module Instantiations and Visits
16.1
Pattern-
Based Macros
16.1.1
define-
syntax-
rule
16.1.2
Lexical Scope
16.1.3
define-
syntax
and
syntax-
rules
16.1.4
Matching Sequences
16.1.5
Identifier Macros
16.1.6
set!
Transformers
16.1.7
Macro-
Generating Macros
16.1.8
Extended Example:
Call-
by-
Reference Functions
On this page:
16.1.1
define-
syntax-
rule
16.1.2
Lexical Scope
16.1.3
define-
syntax
and
syntax-
rules
16.1.4
Matching Sequences
16.1.5
Identifier Macros
16.1.6
set!
Transformers
16.1.7
Macro-
Generating Macros
16.1.8
Extended Example:
Call-
by-
Reference Functions
Racket
top
contents
← prev
up
next →
16.1
Pattern-Based Macros
pattern-based macro
replaces any code that matches a
pattern to an expansion that uses parts of the original syntax that
match parts of the pattern.
16.1.1
define-syntax-rule
The simplest way to create a macro is to use
define-syntax-rule
define-syntax-rule
pattern
template
As a running example, consider the
swap
macro, which swaps
the values stored in two variables. It can be implemented using
define-syntax-rule
as follows:
The macro is “un-Rackety” in the sense that it
involves side effects on variables—
but the point of macros is to let
you add syntactic forms that some other language designer might not
approve.
define-syntax-rule
swap
let
tmp
set!
set!
tmp
The
define-syntax-rule
form binds a macro that matches a
single pattern. The pattern must always start with an open parenthesis
followed by an identifier, which is
swap
in this case. After
the initial identifier, other identifiers are
macro pattern
variables
that can match anything in a use of the macro. Thus, this
macro matches the form
swap
form1
form2
for any
form1
and
form2
Macro pattern variables are similar to pattern variables for
match
. See
Pattern Matching
After the pattern in
define-syntax-rule
is the
template
. The template is used in place of a form that
matches the pattern, except that each instance of a pattern variable
in the template is replaced with the part of the macro use the pattern
variable matched. For example, in
swap
first
last
the pattern variable
matches
first
and
matches
last
, so that the expansion is
let
tmp
first
set!
first
last
set!
last
tmp
16.1.2
Lexical Scope
Suppose that we use the
swap
macro to swap variables named
tmp
and
other
let
tmp
other
swap
tmp
other
list
tmp
other
The result of the above expression should be
(6
5)
. The
naive expansion of this use of
swap
, however, is
let
tmp
other
let
tmp
tmp
set!
tmp
other
set!
other
tmp
list
tmp
other
whose result is
(5
6)
. The problem is that the naive
expansion confuses the
tmp
in the context where
swap
is used with the
tmp
that is in the macro template.
Racket doesn’t produce the naive expansion for the above use of
swap
. Instead, it produces
let
tmp
other
let
tmp_1
tmp
set!
tmp
other
set!
other
tmp_1
list
tmp
other
with the correct result in
(6
5)
. Similarly, in the
example
let
set!
other
swap
set!
other
list
set!
other
the expansion is
let
set!_1
other
let
tmp
set!_1
set!
set!_1
other
set!
other
tmp
list
set!_1
other
so that the local
set!
binding doesn’t interfere with the
assignments introduced by the macro template.
In other words, Racket’s pattern-based macros automatically maintain
lexical scope, so macro implementors can reason about variable
reference in macros and macro uses in the same way as for functions
and function calls.
16.1.3
define-syntax
and
syntax-rules
The
define-syntax-rule
form binds a macro that matches a
single pattern, but Racket’s macro system supports transformers that
match multiple patterns starting with the same identifier. To write
such macros, the programmer must use the more general
define-syntax
form along with the
syntax-rules
transformer form:
define-syntax
id
syntax-rules
literal-id
...
pattern
template
...
The
define-syntax-rule
form is itself a macro
that expands into
define-syntax
with a
syntax-rules
form that contains only one pattern and template.
For example, suppose we would like a
rotate
macro that
generalizes
swap
to work on either two or three identifiers,
so that
let
red
green
blue
rotate
red
green
swaps
rotate
red
green
blue
rotates left
list
red
green
blue
produces
(1
2)
. We can implement
rotate
using
syntax-rules
define-syntax
rotate
syntax-rules
rotate
swap
rotate
begin
swap
swap
The expression
rotate
red
green
matches the first pattern
in the
syntax-rules
form, so it expands to
swap
red
green
. The expression
rotate
red
green
blue
matches the second
pattern, so it expands to
begin
swap
red
green
swap
green
blue
16.1.4
Matching Sequences
A better
rotate
macro would allow any number of identifiers,
instead of just two or three. To match a use of
rotate
with
any number of identifiers, we need a pattern form that has something
like a Kleene star. In a Racket macro pattern, a star is written as
...
To implement
rotate
with
...
, we need a base case to
handle a single identifier, and an inductive case to handle more than
one identifier:
define-syntax
rotate
syntax-rules
rotate
void
rotate
...
begin
swap
rotate
...
When a pattern variable like
is followed by
...
in
a pattern, then it must be followed by
...
in a template,
too. The pattern variable effectively matches a sequence of zero or
more forms, and it is replaced in the template by the same sequence.
Both versions of
rotate
so far are a bit inefficient, since
pairwise swapping keeps moving the value from the first variable into
every variable in the sequence until it arrives at the last one. A
more efficient
rotate
would move the first value directly to
the last variable. We can use
...
patterns to implement the
more efficient variant using a helper macro:
define-syntax
rotate
syntax-rules
rotate
...
shift-to
...
...
define-syntax
shift-to
syntax-rules
shift-to
from0
from
...
to0
to
...
let
tmp
from0
set!
to
from
...
set!
to0
tmp
In the
shift-to
macro,
...
in the template follows
set!
to
from
, which causes the
set!
to
from
expression to be duplicated as many times as necessary to use each
identifier matched in the
to
and
from
sequences. (The number of
to
and
from
matches must
be the same, otherwise the macro expansion fails with an error.)
16.1.5
Identifier Macros
Given our macro definitions, the
swap
or
rotate
identifiers must be used after an open parenthesis, otherwise a syntax
error is reported:
swap
eval:2:0: swap: bad syntax
in: swap
An
identifier macro
is a pattern-matching macro that
works when used by itself without parentheses. For example, we
can define
val
as an identifier macro that expands to
get-val
, so
val
would expand to
get-val
define-syntax
val
lambda
stx
syntax-case
stx
val
identifier?
syntax
val
syntax
get-val
define-values
get-val
put-val!
let
private-val
values
lambda
private-val
lambda
set!
private-val
val
val
The
val
macro uses
syntax-case
, which enables defining more
powerful macros and will be explained in the
Mixing Patterns and Expressions:
syntax-case
section.
For now it is sufficient to know that to define a macro,
syntax-case
is used in a
lambda
, and its templates must be wrapped with an explicit
syntax
constructor. Finally,
syntax-case
clauses
may specify additional guard conditions after the pattern.
Our
val
macro uses an
identifier?
condition to ensure that
val
must not
be used with parentheses. Instead, the macro raises
a syntax error:
val
eval:8:0: val: bad syntax
in: (val)
16.1.6
set!
Transformers
With the above
val
macro, we still must call
put-val!
to
change the stored value. It would be more convenient, however, to use
set!
directly on
val
. To invoke the macro when
val
is
used with
set!
, we create an
assignment transformer
with
make-set!-transformer
We must also declare
set!
as a literal in the
syntax-case
literal list.
define-syntax
val2
make-set!-transformer
lambda
stx
syntax-case
stx
set!
val2
identifier?
syntax
val2
syntax
get-val
set!
val2
syntax
put-val!
val2
val2
set!
val2
10
val2
10
16.1.7
Macro-Generating Macros
Suppose that we have many identifiers like
val
and
val2
that we’d like to redirect to accessor and mutator functions like
get-val
and
put-val!
. We’d like to be able to
just write:
define-get/put-id
val
get-val
put-val!
Naturally, we can implement
define-get/put-id
as a macro:
define-syntax-rule
define-get/put-id
id
get
put!
define-syntax
id
make-set!-transformer
lambda
stx
syntax-case
stx
set!
id
identifier?
syntax
id
syntax
get
set!
id
syntax
put!
define-get/put-id
val3
get-val
put-val!
set!
val3
11
val3
11
The
define-get/put-id
macro is a
macro-generating
macro
16.1.8
Extended Example: Call-by-Reference Functions
We can use pattern-matching macros to add a form to Racket
for defining first-order
call-by-reference
functions. When a
call-by-reference function body mutates its formal argument, the
mutation applies to variables that are supplied as actual arguments in
a call to the function.
For example, if
define-cbr
is like
define
except
that it defines a call-by-reference function, then
define-cbr
swap
let
list
produces
(2
1)
We will implement call-by-reference functions by having function calls
supply accessor and mutators for the arguments, instead of supplying
argument values directly. In particular, for the function
above, we’ll generate
define
do-f
get-a
get-b
put-a!
put-b!
define-get/put-id
get-a
put-a!
define-get/put-id
get-b
put-b!
swap
and redirect a function call
to
do-f
lambda
lambda
lambda
set!
lambda
set!
Clearly, then
define-cbr
is a macro-generating macro, which
binds
to a macro that expands to a call of
do-f
That is,
define-cbr
swap
needs to generate the
definition
define-syntax
syntax-rules
id
actual
...
do-f
lambda
actual
...
lambda
set!
actual
...
At the same time,
define-cbr
needs to define
do-f
using the body of
, this second part is slightly more
complex, so we defer most of it to a
define-for-cbr
helper
module, which lets us write
define-cbr
easily enough:
define-syntax-rule
define-cbr
id
arg
...
body
begin
define-syntax
id
syntax-rules
id
actual
...
...
do-f
lambda
actual
...
...
lambda
set!
actual
...
...
define-for-cbr
do-f
arg
...
explained below...
body
Our remaining task is to define
define-for-cbr
so that it
converts
define-for-cbr
do-f
swap
to the function definition
do-f
above. Most of the work is
generating a
define-get/put-id
declaration for each argument,
and
, and putting them before the body. Normally,
that’s an easy task for
...
in a pattern and template, but
this time there’s a catch: we need to generate the names
get-a
and
put-a!
as well as
get-b
and
put-b!
, and the pattern language provides no way to
synthesize identifiers based on existing identifiers.
As it turns out, lexical scope gives us a way around this problem. The
trick is to iterate expansions of
define-for-cbr
once for
each argument in the function, and that’s why
define-for-cbr
starts with an apparently useless
after the argument
list. We need to keep track of all the arguments seen so far and the
get
and
put
names generated for each, in addition to
the arguments left to process. After we’ve processed all the
identifiers, then we have all the names we need.
Here is the definition of
define-for-cbr
define-syntax
define-for-cbr
syntax-rules
define-for-cbr
do-f
id0
id
...
gens
...
body
define-for-cbr
do-f
id
...
gens
...
id0
get
put
body
define-for-cbr
do-f
id
get
put
...
body
define
do-f
get
...
put
...
define-get/put-id
id
get
put
...
body
Step-by-step, expansion proceeds as follows:
define-for-cbr
do-f
swap
=>
define-for-cbr
do-f
get_1
put_1
swap
=>
define-for-cbr
do-f
get_1
put_1
get_2
put_2
swap
=>
define
do-f
get_1
get_2
put_1
put_2
define-get/put-id
get_1
put_1
define-get/put-id
get_2
put_2
swap
The “subscripts” on
get_1
get_2
put_1
, and
put_2
are inserted by the macro
expander to preserve lexical scope, since the
get
generated by each iteration of
define-for-cbr
should not
bind the
get
generated by a different iteration. In
other words, we are essentially tricking the macro expander into
generating fresh names for us, but the technique illustrates some
of the surprising power of pattern-based macros with automatic
lexical scope.
The last expression eventually expands to just
define
do-f
get_1
get_2
put_1
put_2
let
tmp
get_1
put_1
get_2
put_2
tmp
which implements the call-by-name function
To summarize, then, we can add call-by-reference functions to
Racket with just three small pattern-based macros:
define-cbr
define-for-cbr
, and
define-get/put-id
top
contents
← prev
up
next →
US