500 lines
17 KiB
Racket
500 lines
17 KiB
Racket
#lang scribble/doc
|
|
@(require scribble/manual scribble/eval "guide-utils.rkt")
|
|
|
|
@(define check-eval (make-base-eval))
|
|
@(interaction-eval #:eval check-eval (require (for-syntax racket/base)))
|
|
|
|
@(define-syntax-rule (racketblock/eval #:eval e body ...)
|
|
(begin
|
|
(interaction-eval #:eval e body) ...
|
|
(racketblock body ...)))
|
|
|
|
@title[#:tag "proc-macros" #:style 'toc]{General Macro Transformers}
|
|
|
|
The @racket[define-syntax] form creates a @deftech{transformer
|
|
binding} for an identifier, which is a binding that can be used at
|
|
compile time while expanding expressions to be evaluated at run time.
|
|
The compile-time value associated with a transformer binding can be
|
|
anything; if it is a procedure of one argument, then the binding is
|
|
used as a macro, and the procedure is the @deftech{macro transformer}.
|
|
|
|
The @racket[syntax-rules] and @racket[syntax-id-rules] forms are
|
|
macros that expand to procedure forms. For example, if you evaluate a
|
|
@racket[syntax-rules] form directly (instead of placing on the
|
|
right-hand of a @racket[define-syntax] form), the result is a
|
|
procedure:
|
|
|
|
@interaction[
|
|
(syntax-rules () [(nothing) something])
|
|
]
|
|
|
|
Instead of using @racket[syntax-rules], you can write your own macro
|
|
transformer procedure directly using @racket[lambda]. The argument to
|
|
the procedure is a value that represents the source form, and the
|
|
result of the procedure must be a value that represents the
|
|
replacement form.
|
|
|
|
@local-table-of-contents[]
|
|
|
|
@; ----------------------------------------
|
|
|
|
@section[#:tag "stx-obj"]{Syntax Objects}
|
|
|
|
The input and output of a macro transformer (i.e., source and
|
|
replacement forms) are represented as @deftech{syntax objects}. A
|
|
syntax object contains symbols, lists, and constant values (such as
|
|
numbers) that essentially correspond to the @racket[quote]d form of
|
|
the expression. For example, a representation of the expression
|
|
@racket[(+ 1 2)] contains the symbol @racket['+] and the numbers
|
|
@racket[1] and @racket[2], all in a list. In addition to this quoted
|
|
content, a syntax object associates source-location and
|
|
lexical-binding information with each part of the form. The
|
|
source-location information is used when reporting syntax errors (for
|
|
example), and the lexical-biding information allows the macro system
|
|
to maintain lexical scope. To accommodate this extra information, the
|
|
represention of the expression @racket[(+ 1 2)] is not merely
|
|
@racket['(+ 1 2)], but a packaging of @racket['(+ 1 2)] into a syntax
|
|
object.
|
|
|
|
To create a literal syntax object, use the @racket[syntax] form:
|
|
|
|
@interaction[
|
|
(eval:alts (#,(racket syntax) (+ 1 2)) (syntax (+ 1 2)))
|
|
]
|
|
|
|
In the same way that @litchar{'} abbreviates @racket[quote],
|
|
@litchar{#'} abbreviates @racket[syntax]:
|
|
|
|
@interaction[
|
|
#'(+ 1 2)
|
|
]
|
|
|
|
A syntax object that contains just a symbol is an @deftech{identifier
|
|
syntax object}. Racket provides some additional operations specific to
|
|
identifier syntax objects, including the @racket[identifier?]
|
|
operation to detect identifiers. Most notably,
|
|
@racket[free-identifier=?] determines whether two identifiers refer
|
|
to the same binding:
|
|
|
|
@interaction[
|
|
(identifier? #'car)
|
|
(identifier? #'(+ 1 2))
|
|
(free-identifier=? #'car #'cdr)
|
|
(free-identifier=? #'car #'car)
|
|
(require (only-in racket/base [car also-car]))
|
|
(free-identifier=? #'car #'also-car)
|
|
(free-identifier=? #'car (let ([car 8])
|
|
#'car))
|
|
]
|
|
|
|
The last example above, in particular, illustrates how syntax objects
|
|
preserve lexical-context information.
|
|
|
|
To see the lists, symbols, numbers, @|etc| within a syntax object, use
|
|
@racket[syntax->datum]:
|
|
|
|
@interaction[
|
|
(syntax->datum #'(+ 1 2))
|
|
]
|
|
|
|
The @racket[syntax-e] function is similar to @racket[syntax->datum],
|
|
but it unwraps a single layer of source-location and lexical-context
|
|
information, leaving sub-forms that have their own information wrapped
|
|
as syntax objects:
|
|
|
|
@interaction[
|
|
(syntax-e #'(+ 1 2))
|
|
]
|
|
|
|
The @racket[syntax-e] function always leaves syntax-object wrappers
|
|
around sub-forms that are represented via symbols, numbers, and other
|
|
literal values. The only time it unwraps extra sub-forms is when
|
|
unwrapping a pair, in which case the @racket[cdr] of the pair may be
|
|
recursively unwrapped, depending on how the syntax object was
|
|
constructed.
|
|
|
|
The opposite of @racket[syntax->datum] is, of course,
|
|
@racket[datum->syntax]. In addition to a datum like @racket['(+ 1
|
|
2)], @racket[datum->syntax] needs an existing syntax object to donate
|
|
its lexical context, and optionally another syntax object to donate
|
|
its source location:
|
|
|
|
@interaction[
|
|
(datum->syntax #'lex
|
|
'(+ 1 2)
|
|
#'srcloc)
|
|
]
|
|
|
|
In the above example, the lexical context of @racket[#'lex] is used
|
|
for the new syntax object, while the source location of
|
|
@racket[#'srcloc] is used.
|
|
|
|
When the second (i.e., the ``datum'') argument to
|
|
@racket[datum->syntax] includes syntax objects, those syntax objects
|
|
are preserved intact in the result. That is, deconstructing the result
|
|
with @racket[syntax-e] eventually produces the syntax objects that
|
|
were given to @racket[datum->syntax].
|
|
|
|
|
|
@; ----------------------------------------
|
|
|
|
@section[#:tag "syntax-case"]{Mixing Patterns and Expressions: @racket[syntax-case]}
|
|
|
|
The procedure generated by @racket[syntax-rules] internally uses
|
|
@racket[syntax-e] to deconstruct the given syntax object, and it uses
|
|
@racket[datum->syntax] to construct the result. The
|
|
@racket[syntax-rules] form doesn't provide a way to escape from
|
|
pattern-matching and template-construction mode into an arbitrary
|
|
Racket expression.
|
|
|
|
The @racket[syntax-case] form lets you mix pattern matching, template
|
|
construction, and arbitrary expressions:
|
|
|
|
@specform[(syntax-case stx-expr (literal-id ...)
|
|
[pattern expr]
|
|
...)]
|
|
|
|
Unlike @racket[syntax-rules], the @racket[syntax-case] form does not
|
|
produce a procedure. Instead, it starts with a @racket[_stx-expr]
|
|
expression that determines the syntax object to match against the
|
|
@racket[_pattern]s. Also, each @racket[syntax-case] clause has a
|
|
@racket[_pattern] and @racket[_expr], instead of a @racket[_pattern]
|
|
and @racket[_template]. Within an @racket[_expr], the @racket[syntax]
|
|
form---usually abbreviated with @litchar{#'}---shifts into
|
|
template-construction mode; if the @racket[_expr] of a clause starts
|
|
with @litchar{#'}, then we have something like a @racket[syntax-rules]
|
|
form:
|
|
|
|
@interaction[
|
|
(syntax->datum
|
|
(syntax-case #'(+ 1 2) ()
|
|
[(op n1 n2) #'(- n1 n2)]))
|
|
]
|
|
|
|
We could write the @racket[swap] macro using @racket[syntax-case]
|
|
instead of @racket[define-syntax-rule] or @racket[syntax-rules]:
|
|
|
|
@racketblock[
|
|
(define-syntax swap
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(swap x y) #'(let ([tmp x])
|
|
(set! x y)
|
|
(set! y tmp))])))
|
|
]
|
|
|
|
One advantage of using @racket[syntax-case] is that we can provide
|
|
better error reporting for @racket[swap]. For example, with the
|
|
@racket[define-syntax-rule] definition of @racket[swap], then
|
|
@racket[(swap x 2)] produces a syntax error in terms of @racket[set!],
|
|
because @racket[2] is not an identifier. We can refine our
|
|
@racket[syntax-case] implementation of @racket[swap] to explicitly
|
|
check the sub-forms:
|
|
|
|
@racketblock[
|
|
(define-syntax swap
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(swap x y)
|
|
(if (and (identifier? #'x)
|
|
(identifier? #'y))
|
|
#'(let ([tmp x])
|
|
(set! x y)
|
|
(set! y tmp))
|
|
(raise-syntax-error #f
|
|
"not an identifier"
|
|
stx
|
|
(if (identifier? #'x)
|
|
#'y
|
|
#'x)))])))
|
|
]
|
|
|
|
With this definition, @racket[(swap x 2)] provides a syntax error
|
|
originating from @racket[swap] instead of @racket[set!].
|
|
|
|
In the above definition of @racket[swap], @racket[#'x] and
|
|
@racket[#'y] are templates, even though they are not used as the
|
|
result of the macro transformer. This example illustrates how
|
|
templates can be used to access pieces of the input syntax, in this
|
|
case for checking the form of the pieces. Also, the match for
|
|
@racket[#'x] or @racket[#'y] is used in the call to
|
|
@racket[raise-syntax-error], so that the syntax-error message can
|
|
point directly to the source location of the non-identifier.
|
|
|
|
@; ----------------------------------------
|
|
|
|
@section[#:tag "with-syntax"]{@racket[with-syntax] and @racket[generate-temporaries]}
|
|
|
|
Since @racket[syntax-case] lets us compute with arbitrary Racket
|
|
expression, we can more simply solve a problem that we had in
|
|
writing @racket[define-for-cbr] (see
|
|
@secref["pattern-macro-example"]), where we needed to generate a
|
|
set of names based on a sequence @racket[id ...]:
|
|
|
|
@racketblock[
|
|
(define-syntax (define-for-cbr stx)
|
|
(syntax-case stx ()
|
|
[(_ do-f (id ...) body)
|
|
....
|
|
#'(define (do-f get ... put ...)
|
|
(define-get/put-id id get put) ...
|
|
body) ....]))
|
|
]
|
|
|
|
@margin-note{This example uses @racket[(define-syntax (_id _arg) _body ...+)],
|
|
which is equivalent to @racket[(define-syntax _id (lambda (_arg) _body ...+))].}
|
|
|
|
In place of the @racket[....]s above, we need to bind @racket[get
|
|
...] and @racket[put ...] to lists of generated identifiers. We
|
|
cannot use @racket[let] to bind @racket[get] and @racket[put],
|
|
because we need bindings that count as pattern variables, instead
|
|
of normal local variables. The @racket[with-syntax] form lets us
|
|
bind pattern variables:
|
|
|
|
@racketblock[
|
|
(define-syntax (define-for-cbr stx)
|
|
(syntax-case stx ()
|
|
[(_ do-f (id ...) body)
|
|
(with-syntax ([(get ...) ....]
|
|
[(put ...) ....])
|
|
#'(define (do-f get ... put ...)
|
|
(define-get/put-id id get put) ...
|
|
body))]))
|
|
]
|
|
|
|
Now we need an expression in place of @racket[....] that
|
|
generates as many identifiers as there are @racket[id] matches in
|
|
the original pattern. Since this is a common task, Racket
|
|
provides a helper function, @racket[generate-temporaries], that
|
|
takes a sequence of identifiers and returns a sequence of
|
|
generated identifiers:
|
|
|
|
@racketblock[
|
|
(define-syntax (define-for-cbr stx)
|
|
(syntax-case stx ()
|
|
[(_ do-f (id ...) body)
|
|
(with-syntax ([(get ...) (generate-temporaries #'(id ...))]
|
|
[(put ...) (generate-temporaries #'(id ...))])
|
|
#'(define (do-f get ... put ...)
|
|
(define-get/put-id id get put) ...
|
|
body))]))
|
|
]
|
|
|
|
This way of generating identifiers is normally easier to think
|
|
about than tricking the macro expander into generating names with
|
|
purely pattern-based macros.
|
|
|
|
In general, the right-hand side of a @racket[with-syntax]
|
|
binding is a pattern, just like in @racket[syntax-case]. In fact,
|
|
a @racket[with-syntax] form is just a @racket[syntax-case] form
|
|
turned partially inside-out.
|
|
|
|
@; ----------------------------------------
|
|
|
|
@section[#:tag "stx-phases"]{Compile and Run-Time Phases}
|
|
|
|
As sets of macros get more complicated, you might want to write
|
|
your own helper functions, like
|
|
@racket[generate-temporaries]. For example, to provide good
|
|
syntax-error messsage, @racket[swap], @racket[rotate], and
|
|
@racket[define-cbr] all should check that certain sub-forms in
|
|
the source form are identifiers. We could use a
|
|
@racket[check-ids] to perform this checking everywhere:
|
|
|
|
@racketblock/eval[
|
|
#:eval check-eval
|
|
(define-syntax (swap stx)
|
|
(syntax-case stx ()
|
|
[(swap x y) (begin
|
|
(check-ids stx #'(x y))
|
|
#'(let ([tmp x])
|
|
(set! x y)
|
|
(set! y tmp)))]))
|
|
|
|
(define-syntax (rotate stx)
|
|
(syntax-case stx ()
|
|
[(rotate a c ...)
|
|
(begin
|
|
(check-ids stx #'(a c ...))
|
|
#'(shift-to (c ... a) (a c ...)))]))
|
|
]
|
|
|
|
The @racket[check-ids] function can use the @racket[syntax->list]
|
|
function to convert a syntax-object wrapping a list into a list
|
|
of syntax objects:
|
|
|
|
@racketblock[
|
|
(define (check-ids stx forms)
|
|
(for-each
|
|
(lambda (form)
|
|
(unless (identifier? form)
|
|
(raise-syntax-error #f
|
|
"not an identifier"
|
|
stx
|
|
form)))
|
|
(syntax->list forms)))
|
|
]
|
|
|
|
If you define @racket[swap] and @racket[check-ids] in this way,
|
|
however, it doesn't work:
|
|
|
|
@interaction[
|
|
#:eval check-eval
|
|
(let ([a 1] [b 2]) (swap a b))
|
|
]
|
|
|
|
The problem is that @racket[check-ids] is defined as a run-time
|
|
expression, but @racket[swap] is trying to use it at compile time. In
|
|
interactive mode, compile time and run time are interleaved, but they
|
|
are not interleaved within the body of a module, and they are not
|
|
interleaved or across modules that are compiled ahead-of-time. To help
|
|
make all of these modes treat code consistently, Racket separates the
|
|
binding spaces for different phases.
|
|
|
|
To define a @racket[check-ids] function that can be referenced at
|
|
compile time, use @racket[begin-for-syntax]:
|
|
|
|
@racketblock/eval[
|
|
#:eval check-eval
|
|
(begin-for-syntax
|
|
(define (check-ids stx forms)
|
|
(for-each
|
|
(lambda (form)
|
|
(unless (identifier? form)
|
|
(raise-syntax-error #f
|
|
"not an identifier"
|
|
stx
|
|
form)))
|
|
(syntax->list forms))))
|
|
]
|
|
|
|
With this for-syntax definition, then @racket[swap] works:
|
|
|
|
@interaction[
|
|
#:eval check-eval
|
|
(let ([a 1] [b 2]) (swap a b) (list a b))
|
|
(swap a 1)
|
|
]
|
|
|
|
When organizing a program into modules, you may want to put helper
|
|
functions in one module to be used by macros that reside on other
|
|
modules. In that case, you can write the helper function using
|
|
@racket[define]:
|
|
|
|
@racketmod[#:file
|
|
"utils.rkt"
|
|
racket
|
|
|
|
(provide check-ids)
|
|
|
|
(define (check-ids stx forms)
|
|
(for-each
|
|
(lambda (form)
|
|
(unless (identifier? form)
|
|
(raise-syntax-error #f
|
|
"not an identifier"
|
|
stx
|
|
form)))
|
|
(syntax->list forms)))
|
|
]
|
|
|
|
Then, in the module that implements macros, import the helper function
|
|
using @racket[(require (for-syntax "utils.rkt"))] instead of
|
|
@racket[(require "utils.rkt")]:
|
|
|
|
@racketmod[
|
|
racket
|
|
|
|
(require (for-syntax "utils.rkt"))
|
|
|
|
(define-syntax (swap stx)
|
|
(syntax-case stx ()
|
|
[(swap x y) (begin
|
|
(check-ids stx #'(x y))
|
|
#'(let ([tmp x])
|
|
(set! x y)
|
|
(set! y tmp)))]))
|
|
]
|
|
|
|
Since modules are separately compiled and cannot have circular
|
|
dependencies, the @filepath["utils.rkt"] module's run-time body can be
|
|
compiled before the compiling the module that implements
|
|
@racket[swap]. Thus, the run-time definitions in
|
|
@filepath["utils.rkt"] can be used to implement @racket[swap], as long
|
|
as they are explicitly shifted into compile time by @racket[(require
|
|
(for-syntax ....))].
|
|
|
|
The @racketmodname[racket] module provides @racket[syntax-case],
|
|
@racket[generate-temporaries], @racket[lambda], @racket[if], and more
|
|
for use in both the run-time and compile-time phases. That is why we
|
|
can use @racket[syntax-case] in the @exec{racket} @tech{REPL} both
|
|
directly and in the right-hand side of a @racket[define-syntax]
|
|
form.
|
|
|
|
The @racketmodname[racket/base] module, in contrast, exports those
|
|
bindings only in the run-time phase. If you change the module above
|
|
that defines @racket[swap] so that it uses the
|
|
@racketmodname[racket/base] language instead of
|
|
@racketmodname[racket], then it no longer works. Adding
|
|
@racket[(require (for-syntax racket/base))] imports
|
|
@racket[syntax-case] and more into the compile-time phase, so that the
|
|
module works again.
|
|
|
|
Suppose that @racket[define-syntax] is used to define a local macro in
|
|
the right-hand side of a @racket[define-syntax] form. In that case,
|
|
the right-hand side of the inner @racket[define-syntax] is in the
|
|
@deftech{meta-compile phase level}, also known as @deftech{phase level
|
|
2}. To import @racket[syntax-case] into that phase level, you would
|
|
have to use @racket[(require (for-syntax (for-syntax racket/base)))]
|
|
or, equivalently, @racket[(require (for-meta 2 racket/base))]. For example,
|
|
|
|
@codeblock|{
|
|
#lang racket/base
|
|
(require ;; This provides the bindings for the definition
|
|
;; of shell-game.
|
|
(for-syntax racket/base)
|
|
|
|
;; And this for the definition of
|
|
;; swap.
|
|
(for-syntax (for-syntax racket/base)))
|
|
|
|
(define-syntax (shell-game stx)
|
|
|
|
(define-syntax (swap stx)
|
|
(syntax-case stx ()
|
|
[(_ a b)
|
|
#'(let ([tmp a])
|
|
(set! a b)
|
|
(set! b tmp))]))
|
|
|
|
(syntax-case stx ()
|
|
[(_ a b c)
|
|
(let ([a #'a] [b #'b] [c #'c])
|
|
(when (= 0 (random 2)) (swap a b))
|
|
(when (= 0 (random 2)) (swap b c))
|
|
(when (= 0 (random 2)) (swap a c))
|
|
#`(list #,a #,b #,c))]))
|
|
|
|
(shell-game 3 4 5)
|
|
(shell-game 3 4 5)
|
|
(shell-game 3 4 5)
|
|
}|
|
|
|
|
Negative phase levels also exist. If a macro uses a helper function
|
|
that is imported @racket[for-syntax], and if the helper function
|
|
returns syntax-object constants generated by @racket[syntax], then
|
|
identifiers in the syntax will need bindings at @deftech{phase level
|
|
-1}, also known as the @deftech{template phase level}, to have any
|
|
binding at the run-time phase level relative to the module that
|
|
defines the macro.
|
|
|
|
@; ----------------------------------------
|
|
|
|
@include-section["phases.scrbl"]
|
|
|
|
@; ----------------------------------------
|
|
|
|
@include-section["syntax-taints.scrbl"]
|
|
|
|
@close-eval[check-eval]
|