remove mention of syntax-id-rules in Macros section of Guide

This commit is contained in:
Stephen Chang 2016-07-29 16:19:34 -04:00
parent 169472487e
commit c56efb22a6

View File

@ -1,5 +1,6 @@
#lang scribble/doc #lang scribble/doc
@(require scribble/manual scribble/eval "guide-utils.rkt") @(require scribble/manual scribble/eval scribble/racket "guide-utils.rkt"
(for-syntax racket/base))
@(define swap-eval (make-base-eval)) @(define swap-eval (make-base-eval))
@ -252,77 +253,105 @@ error is reported:
@interaction[#:eval swap-eval (+ swap 3)] @interaction[#:eval swap-eval (+ swap 3)]
An @deftech{identifier macro} is a pattern-matching macro that An @deftech{identifier macro} is a pattern-matching macro that
works in any expression. For example, we works when used by itself without parentheses. For example, we
can define @racket[clock] as an identifier macro that expands to can define @racket[val] as an identifier macro that expands to
@racket[(get-clock)], so @racket[(+ clock 3)] would expand to @racket[(get-val)], so @racket[(+ val 3)] would expand to
@racket[(+ (get-clock) 3)]. An identifier macro also cooperates with @racket[(+ (get-val) 3)].
@racket[set!], and we can define @racket[clock] so that @racket[(set!
clock 3)] expands to @racket[(put-clock! 3)].
The @racket[syntax-id-rules] form is like @racket[syntax-rules], but @interaction-eval[#:eval swap-eval (require (for-syntax racket/base))]
it creates a transformer that acts as an identifier macro: @(define-syntax (with-syntax-as-syntax stx)
(syntax-case stx ()
[(_ e)
(with-syntax ([s (datum->syntax #'e 'syntax)])
#'(let-syntax ([s (make-element-id-transformer
(lambda (stx)
#'@racket[syntax]))]) ;print as syntax not #'
e))]))
@specform[#:literals (syntax-id-rules) @(with-syntax-as-syntax
(define-syntax id @interaction[#:eval swap-eval
(syntax-id-rules (literal-id ...) (define-syntax val
[pattern template] (lambda (stx)
...))] (syntax-case stx ()
[val (identifier? (syntax val)) (syntax (get-val))])))
(define-values (get-val put-val!)
(let ([private-val 0])
(values (lambda () private-val)
(lambda (v) (set! private-val v)))))
val
(+ val 3)])
Unlike a @racket[syntax-rules] form, the @racket[_pattern]s are not The @racket[val] macro uses @racket[syntax-case], which enables defining more
required to start with an open parenthesis. In addition, powerful macros and will be explained in the @secref["syntax-case"] section.
@racket[syntax-id-rules] cooperates specially with @racket[set!], so For now it is sufficient to know that to define a macro, @racket[syntax-case]
that @racket[set!] invokes the macro when @racket[_id] is the target is used in a @racket[lambda], and its templates must be wrapped with an explicit
of an assignment; consequently, @racket[set!] is typically used as a @racket[syntax] constructor. Finally, @racket[syntax-case] clauses
literal with @racket[syntax-id-rules] to match such uses of @racket[set!]. may specify additional guard conditions after the pattern.
@racketblock[ Our @racket[val] macro uses an @racket[identifier?] condition to ensure that
(define-syntax clock @racket[val] @emph{must not} be used with parentheses. Instead, the macro raises
(syntax-id-rules (set!) a syntax error:
[(set! clock e) (put-clock! e)]
[(clock a ...) ((get-clock) a ...)]
[clock (get-clock)]))
(define-values (get-clock put-clock!) @interaction[#:eval swap-eval
(let ([private-clock 0]) (val)]
(values (lambda () private-clock)
(lambda (v) (set! private-clock v))))) @; ----------------------------------------
]
@section{@racket[set!] Transformers}
With the above @racket[val] macro, we still must call @racket[put-val!] to
change the stored value. It would be more convenient, however, to use
@racket[set!] directly on @racket[val]. To invoke the macro when @racket[val] is
used with @racket[set!], we create an
@tech[#:doc '(lib "scribblings/reference/reference.scrbl")]{assignment transformer}
with @racket[make-set!-transformer].
We must also declare @racket[set!] as a literal in the @racket[syntax-case]
literal list.
@(with-syntax-as-syntax
@interaction[#:eval swap-eval
(define-syntax val2
(make-set!-transformer
(lambda (stx)
(syntax-case stx (set!)
[val2 (identifier? (syntax val2)) (syntax (get-val))]
[(set! val2 e) (syntax (put-val! e))]))))
val2
(+ val2 3)
(set! val2 10)
val2])
The @racket[(clock a ...)] pattern is needed because, when an
identifier macro is used after an open parenthesis, the macro
transformer is given the whole form, like with a non-identifier macro.
Put another way, the @racket[syntax-rules] form is essentially a
special case of the @racket[syntax-id-rules] form with errors in the
@racket[set!] and lone-identifier cases.
@; ---------------------------------------- @; ----------------------------------------
@section{Macro-Generating Macros} @section{Macro-Generating Macros}
Suppose that we have many identifiers like @racket[clock] that we'd Suppose that we have many identifiers like @racket[val] and @racket[val2]
like to redirect to accessor and mutator functions like that we'd like to redirect to accessor and mutator functions like
@racket[get-clock] and @racket[put-clock!]. We'd like to be able to @racket[get-val] and @racket[put-val!]. We'd like to be able to
just write just write:
@racketblock[ @racketblock[
(define-get/put-id clock get-clock put-clock!) (define-get/put-id val get-val put-val!)
] ]
Naturally, we can implement @racket[define-get/put-id] as a macro: Naturally, we can implement @racket[define-get/put-id] as a macro:
@racketblock[ @(with-syntax-as-syntax
(define-syntax-rule (define-get/put-id id get put!) @interaction[#:eval swap-eval
(define-syntax-rule (define-get/put-id id get put!)
(define-syntax id (define-syntax id
(syntax-id-rules (set!) (make-set!-transformer
[(set! id e) (put! e)] (lambda (stx)
[(id a (... ...)) ((get) a (... ...))] (syntax-case stx (set!)
[id (get)]))) [id (identifier? (syntax id)) (syntax (get))]
] [(set! id e) (syntax (put! e))])))))
(define-get/put-id val3 get-val put-val!)
(set! val3 11)
val3])
The @racket[define-get/put-id] macro is a @deftech{macro-generating The @racket[define-get/put-id] macro is a @deftech{macro-generating
macro}. The only non-obvious part of its definition is the macro}.
@racket[(... ...)], which ``quotes'' @racket[...] so that it takes its
usual role in the generated macro, instead of the generating macro.
@; ---------------------------------------- @; ----------------------------------------