remove mention of syntax-id-rules in Macros section of Guide
This commit is contained in:
parent
169472487e
commit
c56efb22a6
|
@ -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 id
|
(define-syntax-rule (define-get/put-id id get put!)
|
||||||
(syntax-id-rules (set!)
|
(define-syntax id
|
||||||
[(set! id e) (put! e)]
|
(make-set!-transformer
|
||||||
[(id a (... ...)) ((get) a (... ...))]
|
(lambda (stx)
|
||||||
[id (get)])))
|
(syntax-case stx (set!)
|
||||||
]
|
[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.
|
|
||||||
|
|
||||||
@; ----------------------------------------
|
@; ----------------------------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user