revise phases section in small ways to match the guide's style

This commit is contained in:
Matthew Flatt 2012-03-22 11:34:11 -06:00
parent 16ef341e98
commit c1dbaff6b8
3 changed files with 247 additions and 236 deletions

View File

@ -20,5 +20,4 @@ implemented in Racket---or in a macro-extended variant of Racket.
@;------------------------------------------------------------------------ @;------------------------------------------------------------------------
@include-section["pattern-macros.scrbl"] @include-section["pattern-macros.scrbl"]
@include-section["proc-macros.scrbl"] @include-section["proc-macros.scrbl"]
@include-section["phases.scrbl"]

View File

@ -1,371 +1,379 @@
#lang scribble/manual #lang scribble/manual
@(require scribble/manual
scribble/eval
"guide-utils.rkt"
(for-label syntax/parse))
@title[#:tag "phases"]{Phases} @title[#:tag "phases"]{General Phase Levels}
@(require scribble/manual scribble/eval) A @deftech{phase} can be thought of as a way to separate
computations. Imagine starting two Racket processes. If you ignore
socket communication and the filesystem, the processes will have no
way to share anything. Similarly, Racket effectively allows multiple
invocations of a module to exist in the same process but separated by
phase. Similar to socket communication for processes, module instances
separated by phases can communicate only through the protocol of macro
expansion.
@section{Bindings} @section{Phases and Bindings}
A phase can be thought of as a way to separate computations. Imagine starting Every binding of an identifier exists in a particular phase. The link between
two racket processes. If you ignore socket communication they will not have a a binding and its phase is represented by an integer @deftech{phase
way to share anything. Racket effectively allows multiple invocations to exist level}. Phase level 0 is the phase used for ``plain'' definitions, so
in the same process and phases are the mechanism that keeps them separate, just
like in the multiple process case. Similar to socket communication for processes
phases allow instances of Racket to share information through an explicit
protocol.
Bindings exist in a phase. The link between a binding and its phase is
represented by an integer called a phase level. Phase level 0 is the phase used
for "plain" definitions, so
@racketblock[ @racketblock[
(define age 5) (define age 5)
] ]
Will put a binding for @racket[age] into phase 0. @racket[age] can be defined at adds a binding for @racket[age] into phase level 0. The identifier
higher phases levels easily @racket[age] can be defined at a higher phase level using
@racket[begin-for-syntax]:
@racketblock[ @racketblock[
(begin-for-syntax (begin-for-syntax
(define age 5)) (define age 5))
] ]
Now @racket[age] is defined at phase level 1. We can easily mix these two definitions With a single @racket[begin-for-syntax] wrapper, @racket[age] is
in the same module, there is no clash between the two @racket[age]'s because defined at phase level 1. We can easily mix these two definitions in
they are defined at different phase levels. the same module or top-level namespace, and there is no clash between
the two @racket[age]'s that are defined at different phase levels:
@(define age-eval (make-base-eval)) @(define age-eval (make-base-eval))
@(interaction-eval #:eval age-eval @(interaction-eval #:eval age-eval
(require (for-syntax racket/base))) (require (for-syntax racket/base)))
@examples[#:eval age-eval @interaction[#:eval age-eval
(define age 3) (define age 3)
(begin-for-syntax (begin-for-syntax
(define age 9)) (define age 9))
] ]
@racket[age] at phase level 0 has a value of 3 and @racket[age] at phase level 1 has a value of 9. The @racket[age] binding at phase level 0 has a value of 3, and the
@racket[age] binding at phase level 1 has a value of 9.
Syntax objects can refer to these bindings. Essentially they capture the binding as a value that can be passed around. Syntax objects capture binding information as a first-class value. Thus,
@racketblock[#'age] @racketblock[#'age]
Is a syntax object that represents the @racket[age] binding. But which is a syntax object that represents the @racket[age] binding---but
@racket[age] binding? In the last example there are two @racket[age]'s, one at since there are two @racket[age]'s (one at phase level 0 and one at
phase level 0 and one at phase level 1. Racket will imbue @racket[#'age] with lexical phase level 1), which one does it capture? In fact, Racket imbues
information for all phase levels, so the answer is both! @racket[#'age] with lexical information for all phase levels, so the
answer is that @racket[#'age] captures both!
Racket knows which @racket[age] to use when the syntax object is used. I'll use eval just for a second to prove a point. The relevant binding of @racket[age] captured by @racket[#'age] is
determined when @racket[#'age] is eventually used. As an example, we
bind @racket[#'age] to a pattern variable so we can use it in a
template, and then we @racket[eval]utae the template: @margin-note*{We
use @racket[eval] here to demonstrate phases, but see
@secref["reflection"] for caveats about @racket[eval].}
First we bind @racket[#'age] to a pattern variable so we can use it in a template and then just print it. @interaction[#:eval age-eval
@examples[#:eval age-eval
(eval (with-syntax ([age #'age]) (eval (with-syntax ([age #'age])
#'(printf "~a\n" age))) #'(displayln age)))
] ]
We get 3 because @racket[age] at phase 0 level is bound to 3. The result is 3 because @racket[age] is used at phase 0 level. We can
try again with the use of @racket[age] inside
@racket[begin-for-syntax]:
@examples[#:eval age-eval @interaction[#:eval age-eval
(eval (with-syntax ([age #'age]) (eval (with-syntax ([age #'age])
#'(begin-for-syntax #'(begin-for-syntax
(printf "~a\n" age)))) (displayln age))))
] ]
We get 9 because we are using @racket[age] at phase level 1 instead of 0. How In this case, the answer is 9, because we are using @racket[age] at
does Racket know we wanted to use @racket[age] at phase 1 instead of 0? Because phase level 1 instead of 0 (i.e., @racket[begin-for-syntax] evaluates its
of the @racket[begin-for-syntax]. @racket[begin-for-syntax] evaluates the expressions at phase level 1). So, you can see that we started with the
expressions inside it at phase level 1. So you can see that we started with the same syntax object, @racket[#'age], and we were able to use it in two
same syntax object, @racket[#'age], and was able to use it in two different ways different ways: at phase level 0 and at phase level 1.
-- at phase level 0 and at phase level 1.
When a syntax object is created its lexical context is immediately set up. A syntax object has a lexical context from the moment it first exists.
Syntax objects provided from a module retain their lexical context and will A syntax objects that is provided from a module retains its lexical
reference bindings that existed in the module it came from. context, and so it references bindings in the source module, not the use context.
The following example defines @racket[button] at phase
level 0 bound to the value 0, while @racket[see-button] binds the
syntax object for @racket[button] in module @racket[a]:
The following example defines @racket[button] at phase level 0 bound to the @interaction[
value 0 and @racket[sbutton] which binds the syntax object for @racket[button]
in module @racket[a].
@examples[
(module a racket (module a racket
(define button 0) (define button 0)
(provide (for-syntax sbutton)) (provide (for-syntax see-button))
@code:comment{why not (define sbutton #'button) ? I will explain later} @code:comment[@#,t{Why not @racket[(define see-button #'button)]? We explain later.}]
(define-for-syntax sbutton #'button) (define-for-syntax see-button #'button))
)
(module b racket (module b racket
(require 'a) (require 'a)
(define button 8) (define button 8)
(define-syntax (m stx) (define-syntax (m stx)
sbutton) see-button)
(m) (m))
)
(require 'b) (require 'b)
] ]
The result of the @racket[m] macro will be whatever value @racket[sbutton] is The result of the @racket[m] macro is the value of
bound to, which is @racket[#'button]. The @racket[#'button] that @racket[see-button], which is @racket[#'button] with the lexical
@racket[sbutton] knows that @racket[button] is bound from the @racket[a] module context of the @racket[a] module. Even though there is another
at phase level 0. Even though there is another @racket[button] in @racket[b] @racket[button] in @racket[b], the second @racket[button] will not
this will not confuse Racket. confuse Racket, because the lexical context of @racket[#'button] is
@racket[a].
Note that @racket[sbutton] is bound at phase level 1 by virtue of defining it with Note that @racket[see-button] is bound at phase level 1 by virtue of
@racket[define-for-syntax]. This is needed because @racket[m] is a macro so its defining it with @racket[define-for-syntax]. Phase level 1 is needed
body executes at one phase higher than it was defined at. Since it was defined because @racket[m] is a macro, so its body executes at one phase
at phase level 0 the body will execute at phase level 1, so any bindings in the body also higher than the context of its definition. Since @racket[m] is defined
need to be bound at phase level 1. at phase level 0, its body is at phase level 1, so any bindings
referenced by the body must be at phase level 1.
@section{Modules} @; ======================================================================
Phases and bindings can get very confusing when used with modules. Racket allows @section{Phases and Modules}
us to import a module at an arbitrary phase using require.
A @tech{phase level} is a module-relative concept. When importing from
an other module via @racket[require], Racket lets us shift imported
bindings to a different phase level than the original one:
@racketblock[ @racketblock[
(require "a.rkt") @code:comment{import at phase 0} (require "a.rkt") @code:comment{import with no phase shift}
(require (for-syntax "a.rkt")) @code:comment{import at phase 1} (require (for-syntax "a.rkt")) @code:comment{shift phase by +1}
(require (for-template "a.rkt")) @code:comment{import at phase -1} (require (for-template "a.rkt")) @code:comment{shift phase by -1}
(require (for-meta 5 "a.rkt" )) @code:comment{import at phase 5} (require (for-meta 5 "a.rkt" )) @code:comment{shift phase by +5}
] ]
What does it mean to 'import at phase 1'? Effectively it means that all the That is, using @racket[for-syntax] in @racket[require] means that all
bindings from that module will have their phase increased by one. Similarly when of the bindings from that module will have their phase levels increased by
importing at phase -1 all bindings from that module will have their phase one. A binding that is @racket[defined] at phase level 0
decreased by one. and imported with @racket[for-syntax] becomes a phase-level 1 binding:
@examples[ @interaction[
(module c racket (module c racket
(define x 0) ;; x is defined at phase 0 (define x 0) @code:comment{defined at phase level 0}
(provide x) (provide x))
)
(module d racket (module d racket
(require (for-syntax 'c)) (require (for-syntax 'c))
) @code:comment{has a binding at phase level 1, not 0:}
#'x)
] ]
Now in the @racket[d] module there will be a binding for @racket[x] at phase 1 instead of phase 0. Let's see what happens if we try to create a binding for the
@racket[#'button] syntax object at phase level 0:
So lets look at module @racket[a] from above and see what happens if we try to create a
binding for the @racket[#'button] syntax object at phase 0.
@(define button-eval (make-base-eval)) @(define button-eval (make-base-eval))
@(interaction-eval #:eval button-eval @(interaction-eval #:eval button-eval
(require (for-syntax racket/base))) (require (for-syntax racket/base)))
@examples[#:eval button-eval @interaction[#:eval button-eval
(define button 0) (define button 0)
(define sbutton #'button) (define see-button #'button)
] ]
Now both @racket[button] and @racket[sbutton] are defined at phase 0. The lexical Now both @racket[button] and @racket[see-button] are defined at phase
context of @racket[#'button] will know that there is a binding for 0. The lexical context of @racket[#'button] will know that there is a
@racket[button] at binding for @racket[button] at phase 0. In fact it seems like things
phase 0. In fact it seems like things are working just fine, if we try to eval are working just fine if we try to @racket[eval] @racket[see-button]:
@racket[sbutton] here we will get 0.
@examples[#:eval button-eval @interaction[#:eval button-eval
(eval sbutton) (eval see-button)
] ]
But now lets use sbutton in a macro. Now, let's use @racket[see-button] in a macro:
@examples[#:eval button-eval @interaction[#:eval button-eval
(define-syntax (m stx) (define-syntax (m stx)
sbutton) see-button)
(m) (m)
] ]
We get an error 'reference to an identifier before its definition: sbutton'. Clearly @racket[see-button] is not defined at phase level 1, so we cannot refer to
Clearly @racket[sbutton] is not defined at phase level 1 so we cannot refer to it inside the macro body. Let's try to use @racket[see-button] in another module by
it inside the macro. Lets try to use @racket[sbutton] in another module by
putting the button definitions in a module and importing it at phase level 1. putting the button definitions in a module and importing it at phase level 1.
Then we will get @racket[sbutton] at phase level 1. Then, we will get @racket[see-button] at phase level 1:
@examples[ @interaction[
(module a racket (module a racket
(define button 0) (define button 0)
(define sbutton #'button) (define see-button #'button)
(provide sbutton)) (provide see-button))
(module b racket (module b racket
(require (for-syntax 'a)) ;; now we have sbutton at phase level 1 (require (for-syntax 'a)) @code:comment[@#,t{gets @racket[see-button] at phase level 1}]
(define-syntax (m stx) (define-syntax (m stx)
sbutton) see-button)
(m) (m))
)
] ]
Racket says that @racket[button] is unbound now. When @racket[a] is imported at Racket says that @racket[button] is unbound now! When @racket[a] is imported at
phase level 1 we have the following bindings phase level 1, we have the following bindings:
@verbatim{ @racketblock[
button at phase level 1 button @#,elem{at phase level 1}
sbutton at phase level 1 see-button @#,elem{at phase level 1}
} ]
So the macro @racket[m] can see a binding for @racket[sbutton] at phase level 1 So, the macro @racket[m] can see a binding for @racket[see-button] at
and will return the @racket[#'button] syntax object which refers to phase level 1 and will return the @racket[#'button] syntax object,
@racket[button] binding at phase 0. But there is no @racket[button] at phase which refers to @racket[button] binding at phase level 1. But the use
level 0 in @racket[b], there is only a @racket[button] at phase 1, so we get an of @racket[m] is at phase level 0, and there is no @racket[button] at
error. That is why @racket[sbutton] needed to be bound at phase 1 in phase level 0 in @racket[b]. That is why @racket[see-button] needs to
@racket[a]. In that case we would have had the following bindings after doing be bound at phase level 1, as in the original @racket[a]. In the original @racket[b], then,
@racket[(require 'a)] we have the following bindings:
@verbatim{ @racketblock[
button at phase level 0 button @#,elem{at phase level 0}
sbutton at phase level 1 see-button @#,elem{at phase level 1}
} ]
In this scenario we can use @racket[sbutton] in the macro since @racket[sbutton] In this scenario, we can use @racket[see-button] in the macro, since
is bound at phase level 1 and when the macro finishes it will refer to a @racket[see-button] is bound at phase level 1. When the macro expands,
@racket[button] binding at phase level 0. it will refer to a @racket[button] binding at phase level 0.
However, if we import @racket[a] at phase level 1 we can still manage to use Defining @racket[see-button] with @racket[(define see-button
@racket[sbutton] to get access to @racket[button]. The trick is to create a #'button)] isn't inherently wrong; it depends on how we intend
syntax object that will be evaluated at phase level 1 instead of 0. We can do @racket[see-button] to be used. For example, we can arrange for
that with @racket[begin-for-syntax]. @racket[m] to sensibly use @racket[see-button] because it puts it in a
phase level 1 context using @racket[begin-for-syntax]:
@examples[ @interaction[
(module a racket (module a racket
(define button 0) (define button 0)
(define sbutton #'button) (define see-button #'button)
(provide sbutton)) (provide see-button))
(module b racket (module b racket
(require (for-syntax 'a)) (require (for-syntax 'a))
(define-syntax (m stx) (define-syntax (m stx)
(with-syntax ([x sbutton]) (with-syntax ([x see-button])
#'(begin-for-syntax #'(begin-for-syntax
(printf "~a\n" x)))) (displayln x))))
(m)) (m))
] ]
Module @racket[b] has @racket[button] and @racket[sbutton] bound at phase level 1. The output of the macro will be In this case, module @racket[b] has both @racket[button] and
@racket[see-button] bound at phase level 1. The expansion of the macro
is
@racketblock[ @racketblock[
(begin-for-syntax (begin-for-syntax
(printf "~a\n" button)) (displayln button))
] ]
Because @racket[sbutton] will turn into @racket[button] when the template is expanded. which works, because @racket[button] is bound at phase level 1.
Now this expression will work because @racket[button] is bound at phase level 1.
Now you might try to cheat the phase system by importing @racket[a] at both Now, you might try to cheat the phase system by importing @racket[a] at both
phase level 0 and phase level 1. Then you would have the following bindings phase level 0 and phase level 1. Then you would have the following bindings
@verbatim{ @racketblock[
button at phase level 0 button @#,elem{at phase level 0}
sbutton at phase level 0 see-button @#,elem{at phase level 0}
button at phase level 1 button @#,elem{at phase level 1}
sbutton at phase level 1 see-button @#,elem{at phase level 1}
} ]
So just using @racket[sbutton] in a macro should work In that case you might expect that @racket[see-button] in a macro should
work, but it doesn't:
@examples[ @interaction[
(module a racket (module a racket
(define button 0) (define button 0)
(define sbutton #'button) (define see-button #'button)
(provide sbutton)) (provide see-button))
(module b racket (module b racket
(require 'a (require 'a
(for-syntax 'a)) (for-syntax 'a))
(define-syntax (m stx) (define-syntax (m stx)
sbutton) see-button)
(m)) (m))
] ]
The @racket[sbutton] inside the @racket[m] macro comes from the The @racket[see-button] inside the @racket[m] macro comes from the
@racket[(for-syntax 'a)]. For this macro to work there must be a @racket[button] @racket[(for-syntax 'a)] import. For the macro to work, there must be a @racket[button]
at phase 0 bound, and there is one from the plain @racket[(require 'a)] imported at phase 0 bound, and there is such a binding implied by @racket[(require 'a)].
at phase 0. But in fact this macro doesn't work, it says @racket[button] is However, @racket[(require 'a)] and @racket[(require (for-syntax
unbound. The key is that @racket[(require 'a)] and @racket[(require (for-syntax 'a))] are different instantiations of the same module. The @racket[see-button] at
'a))] are different instantiations of the same module. The @racket[sbutton] at
phase 1 only refers to the @racket[button] at phase level 1, not the @racket[button] phase 1 only refers to the @racket[button] at phase level 1, not the @racket[button]
bound at phase 0 from a different instantation, even from the same file. bound at phase 0 from a different instantiation---even from the same source module.
So this means that if you have a two functions in a module, one that produces a Mismatches like the one above can easily show up when a macro tries to
syntax object and one that matches on it (say using @racket[syntax/parse]) the match literal bindings---using @racket[syntax-case] or
module needs to be imported once at the proper phase. The module can't be @racket[syntax/parse].
imported once at phase 0 and again at phase level 1 and be expected to work.
@examples[ @interaction[
(module x racket (module x racket
(require (for-syntax syntax/parse)
(require (for-syntax syntax/parse) (for-template racket/base))
(for-template racket/base))
(provide (all-defined-out)) (provide (all-defined-out))
(define button 0) (define button 0)
(define (make) #'button) (define (make) #'button)
(define-syntax (process stx) (define-syntax (process stx)
(define-literal-set locals (button)) (define-literal-set locals (button))
(syntax-parse stx (syntax-parse stx
[(_ (n (~literal button))) #'#''ok]))) [(_ (n (~literal button))) #'#''ok])))
(module y racket (module y racket
(require (for-meta 1 'x) (require (for-meta 1 'x)
(for-meta 2 'x racket/base) (for-meta 2 'x racket/base))
;; (for-meta 2 racket/base)
)
(begin-for-syntax (begin-for-syntax
(define-syntax (m stx) (define-syntax (m stx)
(with-syntax ([out (make)]) (with-syntax ([out (make)])
#'(process (0 out))))) #'(process (0 out)))))
(define-syntax (p stx) (define-syntax (p stx)
(m)) (m))
(p)) (p))
] ]
@racket[make] is being used in @racket[y] at phase 2 and returns the In this example, @racket[make] is being used in @racket[y] at phase
@racket[#'button] syntax object which refers to @racket[button] bound at phase level 2, and it returns the @racket[#'button] syntax object---which
level 0 inside @racket[x] and at phase 2 in @racket[y] from @racket[(for-meta 2 refers to @racket[button] bound at phase level 0 inside @racket[x] and
'x)]. The @racket[process] macro is imported at phase level 1 from at phase level 2 in @racket[y] from @racket[(for-meta 2 'x)]. The
@racket[(for-meta 1 'x)] and knows that @racket[button] should be bound at phase @racket[process] macro is imported at phase level 1 from
level 1 so when the @racket[syntax-parse] is executed inside @racket[process] it @racket[(for-meta 1 'x)], and it knows that @racket[button] should be
is looking for @racket[button] bound at phase level 1 but it sees a phase level bound at phase level 1. When the @racket[syntax-parse] is executed
2 binding and so doesn't match. inside @racket[process], it is looking for @racket[button] bound at
phase level 1 but it sees only a phase level 2 binding and doesn't
match.
To fix this we can provide @racket[make] at phase level 1 relative to @racket[x] and To fix the example, we can provide @racket[make] at phase level 1
just import it at phase level 1 in @racket[y]. relative to @racket[x], and then we import it at phase level 1 in
@racket[y]:
@examples[ @interaction[
(module x racket (module x racket
(require (for-syntax syntax/parse) (require (for-syntax syntax/parse)
(for-template racket/base)) (for-template racket/base))
(provide (all-defined-out)) (provide (all-defined-out))
(define button 0) (define button 0)
(provide (for-syntax make))
(define-for-syntax (make) #'button) (provide (for-syntax make))
(define-syntax (process stx) (define-for-syntax (make) #'button)
(define-literal-set locals (button)) (define-syntax (process stx)
(syntax-parse stx (define-literal-set locals (button))
[(_ (n (~literal button))) #'#''ok]))) (syntax-parse stx
[(_ (n (~literal button))) #'#''ok])))
(module y racket (module y racket
(require (for-meta 1 'x) (require (for-meta 1 'x)
;; (for-meta 2 'x racket/base) (for-meta 2 racket/base))
(for-meta 2 racket/base)
)
(begin-for-syntax
(define-syntax (m stx)
(with-syntax ([out (make)])
#'(process (0 out)))))
(define-syntax (p stx)
(m))
(p)) (begin-for-syntax
(define-syntax (m stx)
(with-syntax ([out (make)])
#'(process (0 out)))))
(define-syntax (p stx)
(m))
(p))
(require 'y) (require 'y)
] ]

View File

@ -490,6 +490,10 @@ defines the macro.
@; ---------------------------------------- @; ----------------------------------------
@include-section["phases.scrbl"]
@; ----------------------------------------
@include-section["syntax-taints.scrbl"] @include-section["syntax-taints.scrbl"]
@close-eval[check-eval] @close-eval[check-eval]