|
|
|
|
# General Phase Levels
|
|
|
|
|
|
|
|
|
|
A _phase_ can be thought of as a way to separate computations in a
|
|
|
|
|
pipeline of processes where one produces code that is used by the next.
|
|
|
|
|
\(E.g., a pipeline that consists of a preprocessor process, a compiler,
|
|
|
|
|
and an assembler.\)
|
|
|
|
|
|
|
|
|
|
Imagine starting two Racket processes for this purpose. If you ignore
|
|
|
|
|
inter-process communication channels like sockets and files, the
|
|
|
|
|
processes will have no way to share anything other than the text that is
|
|
|
|
|
piped from the standard output of one process into the standard input of
|
|
|
|
|
the other. Similarly, Racket effectively allows multiple invocations of
|
|
|
|
|
a module to exist in the same process but separated by phase. Racket
|
|
|
|
|
enforces _separation_ of such phases, where different phases cannot
|
|
|
|
|
communicate in any way other than via the protocol of macro expansion,
|
|
|
|
|
where the output of one phases is the code used in the next.
|
|
|
|
|
|
|
|
|
|
## 1. Phases and Bindings
|
|
|
|
|
|
|
|
|
|
Every binding of an identifier exists in a particular phase. The link
|
|
|
|
|
between a binding and its phase is represented by an integer _phase
|
|
|
|
|
level_. Phase level 0 is the phase used for “plain” \(or “runtime”\)
|
|
|
|
|
definitions, so
|
|
|
|
|
|
|
|
|
|
`(define` `age` `5)`
|
|
|
|
|
|
|
|
|
|
adds a binding for `age` into phase level 0. The identifier `age` can
|
|
|
|
|
be defined at a higher phase level using `begin-for-syntax`:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(begin-for-syntax
|
|
|
|
|
(define age 5))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
With a single `begin-for-syntax` wrapper, `age` is defined at phase
|
|
|
|
|
level 1. We can easily mix these two definitions in the same module or
|
|
|
|
|
in a top-level namespace, and there is no clash between the two `age`s
|
|
|
|
|
that are defined at different phase levels:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (define age 3)
|
|
|
|
|
> (begin-for-syntax
|
|
|
|
|
(define age 9))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The `age` binding at phase level 0 has a value of 3, and the `age`
|
|
|
|
|
binding at phase level 1 has a value of 9.
|
|
|
|
|
|
|
|
|
|
Syntax objects capture binding information as a first-class value. Thus,
|
|
|
|
|
|
|
|
|
|
`#'age`
|
|
|
|
|
|
|
|
|
|
is a syntax object that represents the `age` binding—but since there are
|
|
|
|
|
two `age`s \(one at phase level 0 and one at phase level 1\), which one
|
|
|
|
|
does it capture? In fact, Racket imbues `#'age` with lexical
|
|
|
|
|
information for all phase levels, so the answer is that `#'age` captures
|
|
|
|
|
both.
|
|
|
|
|
|
|
|
|
|
The relevant binding of `age` captured by `#'age` is determined when
|
|
|
|
|
`#'age` is eventually used. As an example, we bind `#'age` to a pattern
|
|
|
|
|
variable so we can use it in a template, and then we `eval`uate the
|
|
|
|
|
template: We use `eval` here to demonstrate phases, but see \[missing\]
|
|
|
|
|
for caveats about `eval`.
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (eval (with-syntax ([age #'age])
|
|
|
|
|
#'(displayln age)))
|
|
|
|
|
3
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The result is `3` because `age` is used at phase 0 level. We can try
|
|
|
|
|
again with the use of `age` inside `begin-for-syntax`:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (eval (with-syntax ([age #'age])
|
|
|
|
|
#'(begin-for-syntax
|
|
|
|
|
(displayln age))))
|
|
|
|
|
9
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In this case, the answer is `9`, because we are using `age` at phase
|
|
|
|
|
level 1 instead of 0 \(i.e., `begin-for-syntax` evaluates its
|
|
|
|
|
expressions at phase level 1\). So, you can see that we started with the
|
|
|
|
|
same syntax object, `#'age`, and we were able to use it in two different
|
|
|
|
|
ways: at phase level 0 and at phase level 1.
|
|
|
|
|
|
|
|
|
|
A syntax object has a lexical context from the moment it first exists. A
|
|
|
|
|
syntax object that is provided from a module retains its lexical
|
|
|
|
|
context, and so it references bindings in the context of its source
|
|
|
|
|
module, not the context of its use. The following example defines
|
|
|
|
|
`button` at phase level 0 and binds it to `0`, while `see-button` binds
|
|
|
|
|
the syntax object for `button` in module `a`:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (module a racket
|
|
|
|
|
(define button 0)
|
|
|
|
|
(provide (for-syntax see-button))
|
|
|
|
|
; Why not (define see-button #'button)? We explain later.
|
|
|
|
|
(define-for-syntax see-button #'button))
|
|
|
|
|
> (module b racket
|
|
|
|
|
(require 'a)
|
|
|
|
|
(define button 8)
|
|
|
|
|
(define-syntax (m stx)
|
|
|
|
|
see-button)
|
|
|
|
|
(m))
|
|
|
|
|
> (require 'b)
|
|
|
|
|
0
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The result of the `m` macro is the value of `see-button`, which is
|
|
|
|
|
`#'button` with the lexical context of the `a` module. Even though
|
|
|
|
|
there is another `button` in `b`, the second `button` will not confuse
|
|
|
|
|
Racket, because the lexical context of `#'button` \(the value bound to
|
|
|
|
|
`see-button`\) is `a`.
|
|
|
|
|
|
|
|
|
|
Note that `see-button` is bound at phase level 1 by virtue of defining
|
|
|
|
|
it with `define-for-syntax`. Phase level 1 is needed because `m` is a
|
|
|
|
|
macro, so its body executes at one phase higher than the context of its
|
|
|
|
|
definition. Since `m` is defined at phase level 0, its body is at phase
|
|
|
|
|
level 1, so any bindings referenced by the body must be at phase level
|
|
|
|
|
1.
|
|
|
|
|
|
|
|
|
|
## 2. Phases and Modules
|
|
|
|
|
|
|
|
|
|
A phase level is a module-relative concept. When importing from another
|
|
|
|
|
module via `require`, Racket lets us shift imported bindings to a phase
|
|
|
|
|
level that is different from the original one:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(require "a.rkt") ; import with no phase shift
|
|
|
|
|
(require (for-syntax "a.rkt")) ; shift phase by +1
|
|
|
|
|
(require (for-template "a.rkt")) ; shift phase by -1
|
|
|
|
|
(require (for-meta 5 "a.rkt")) ; shift phase by +5
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
That is, using `for-syntax` in `require` means that all of the bindings
|
|
|
|
|
from that module will have their phase levels increased by one. A
|
|
|
|
|
binding that is `define`d at phase level 0 and imported with
|
|
|
|
|
`for-syntax` becomes a phase-level 1 binding:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (module c racket
|
|
|
|
|
(define x 0) ; defined at phase level 0
|
|
|
|
|
(provide x))
|
|
|
|
|
> (module d racket
|
|
|
|
|
(require (for-syntax 'c))
|
|
|
|
|
; has a binding at phase level 1, not 0:
|
|
|
|
|
#'x)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Let’s see what happens if we try to create a binding for the `#'button`
|
|
|
|
|
syntax object at phase level 0:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (define button 0)
|
|
|
|
|
> (define see-button #'button)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Now both `button` and `see-button` are defined at phase 0. The lexical
|
|
|
|
|
context of `#'button` will know that there is a binding for `button` at
|
|
|
|
|
phase 0. In fact, it seems like things are working just fine if we try
|
|
|
|
|
to `eval` `see-button`:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (eval see-button)
|
|
|
|
|
0
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Now, let’s use `see-button` in a macro:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (define-syntax (m stx)
|
|
|
|
|
see-button)
|
|
|
|
|
> (m)
|
|
|
|
|
see-button: undefined;
|
|
|
|
|
cannot reference an identifier before its definition
|
|
|
|
|
in module: top-level
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Clearly, `see-button` is not defined at phase level 1, so we cannot
|
|
|
|
|
refer to it inside the macro body. Let’s try to use `see-button` in
|
|
|
|
|
another module by putting the button definitions in a module and
|
|
|
|
|
importing it at phase level 1. Then, we will get `see-button` at phase
|
|
|
|
|
level 1:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (module a racket
|
|
|
|
|
(define button 0)
|
|
|
|
|
(define see-button #'button)
|
|
|
|
|
(provide see-button))
|
|
|
|
|
> (module b racket
|
|
|
|
|
(require (for-syntax 'a)) ; gets see-button at phase level 1
|
|
|
|
|
(define-syntax (m stx)
|
|
|
|
|
see-button)
|
|
|
|
|
(m))
|
|
|
|
|
eval:1:0: button: unbound identifier;
|
|
|
|
|
also, no #%top syntax transformer is bound
|
|
|
|
|
in: button
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Racket says that `button` is unbound now! When `a` is imported at phase
|
|
|
|
|
level 1, we have the following bindings:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
button at phase level 1
|
|
|
|
|
see-button at phase level 1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
So the macro `m` can see a binding for `see-button` at phase level 1 and
|
|
|
|
|
will return the `#'button` syntax object, which refers to `button`
|
|
|
|
|
binding at phase level 1. But the use of `m` is at phase level 0, and
|
|
|
|
|
there is no `button` at phase level 0 in `b`. That is why `see-button`
|
|
|
|
|
needs to be bound at phase level 1, as in the original `a`. In the
|
|
|
|
|
original `b`, then, we have the following bindings:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
button at phase level 0
|
|
|
|
|
see-button at phase level 1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In this scenario, we can use `see-button` in the macro, since
|
|
|
|
|
`see-button` is bound at phase level 1. When the macro expands, it will
|
|
|
|
|
refer to a `button` binding at phase level 0.
|
|
|
|
|
|
|
|
|
|
Defining `see-button` with `(define see-button #'button)` isn’t
|
|
|
|
|
inherently wrong; it depends on how we intend to use `see-button`. For
|
|
|
|
|
example, we can arrange for `m` to sensibly use `see-button` because it
|
|
|
|
|
puts it in a phase level 1 context using `begin-for-syntax`:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (module a racket
|
|
|
|
|
(define button 0)
|
|
|
|
|
(define see-button #'button)
|
|
|
|
|
(provide see-button))
|
|
|
|
|
> (module b racket
|
|
|
|
|
(require (for-syntax 'a))
|
|
|
|
|
(define-syntax (m stx)
|
|
|
|
|
(with-syntax ([x see-button])
|
|
|
|
|
#'(begin-for-syntax
|
|
|
|
|
(displayln x))))
|
|
|
|
|
(m))
|
|
|
|
|
0
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In this case, module `b` has both `button` and `see-button` bound at
|
|
|
|
|
phase level 1. The expansion of the macro is
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
(begin-for-syntax
|
|
|
|
|
(displayln button))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
which works, because `button` is bound at phase level 1.
|
|
|
|
|
|
|
|
|
|
Now, you might try to cheat the phase system by importing `a` at both
|
|
|
|
|
phase level 0 and phase level 1. Then you would have the following
|
|
|
|
|
bindings
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
button at phase level 0
|
|
|
|
|
see-button at phase level 0
|
|
|
|
|
button at phase level 1
|
|
|
|
|
see-button at phase level 1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You might expect now that `see-button` in a macro would work, but it
|
|
|
|
|
doesn’t:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (module a racket
|
|
|
|
|
(define button 0)
|
|
|
|
|
(define see-button #'button)
|
|
|
|
|
(provide see-button))
|
|
|
|
|
> (module b racket
|
|
|
|
|
(require 'a
|
|
|
|
|
(for-syntax 'a))
|
|
|
|
|
(define-syntax (m stx)
|
|
|
|
|
see-button)
|
|
|
|
|
(m))
|
|
|
|
|
eval:1:0: button: unbound identifier;
|
|
|
|
|
also, no #%top syntax transformer is bound
|
|
|
|
|
in: button
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
The `see-button` inside macro `m` comes from the `(for-syntax 'a)`
|
|
|
|
|
import. For macro `m` to work, it needs to have `button` bound at phase
|
|
|
|
|
0. That binding exists—it’s implied by `(require 'a)`. However,
|
|
|
|
|
`(require 'a)` and `(require (for-syntax 'a))` are _different
|
|
|
|
|
instantiations_ of the same module. The `see-button` at phase 1 only
|
|
|
|
|
refers to the `button` at phase 1, not the `button` bound at phase 0
|
|
|
|
|
from a different instantiation—even from the same source module.
|
|
|
|
|
|
|
|
|
|
This kind of phase-level mismatch between instantiations can be repaired
|
|
|
|
|
with `syntax-shift-phase-level`. Recall that a syntax object like
|
|
|
|
|
`#'button` captures lexical information at _all_ phase levels. The
|
|
|
|
|
problem here is that `see-button` is invoked at phase 1, but needs to
|
|
|
|
|
return a syntax object that can be evaluated at phase 0. By default,
|
|
|
|
|
`see-button` is bound to `#'button` at the same phase level. But with
|
|
|
|
|
`syntax-shift-phase-level`, we can make `see-button` refer to `#'button`
|
|
|
|
|
at a different relative phase level. In this case, we use a phase shift
|
|
|
|
|
of `-1` to make `see-button` at phase 1 refer to `#'button` at phase 0.
|
|
|
|
|
\(Because the phase shift happens at every level, it will also make
|
|
|
|
|
`see-button` at phase 0 refer to `#'button` at phase -1.\)
|
|
|
|
|
|
|
|
|
|
Note that `syntax-shift-phase-level` merely creates a reference across
|
|
|
|
|
phases. To make that reference work, we still need to instantiate our
|
|
|
|
|
module at both phases so the reference and its target have their
|
|
|
|
|
bindings available. Thus, in module `'b`, we still import module `'a` at
|
|
|
|
|
both phase 0 and phase 1—using `(require 'a (for-syntax 'a))`—so we have
|
|
|
|
|
a phase-1 binding for `see-button` and a phase-0 binding for `button`.
|
|
|
|
|
Now macro `m` will work.
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (module a racket
|
|
|
|
|
(define button 0)
|
|
|
|
|
(define see-button (syntax-shift-phase-level #'button -1))
|
|
|
|
|
(provide see-button))
|
|
|
|
|
> (module b racket
|
|
|
|
|
(require 'a (for-syntax 'a))
|
|
|
|
|
(define-syntax (m stx)
|
|
|
|
|
see-button)
|
|
|
|
|
(m))
|
|
|
|
|
> (require 'b)
|
|
|
|
|
0
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
By the way, what happens to the `see-button` that’s bound at phase 0?
|
|
|
|
|
Its `#'button` binding has likewise been shifted, but to phase -1. Since
|
|
|
|
|
`button` itself isn’t bound at phase -1, if we try to evaluate
|
|
|
|
|
`see-button` at phase 0, we get an error. In other words, we haven’t
|
|
|
|
|
permanently cured our mismatch problem—we’ve just shifted it to a less
|
|
|
|
|
bothersome location.
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (module a racket
|
|
|
|
|
(define button 0)
|
|
|
|
|
(define see-button (syntax-shift-phase-level #'button -1))
|
|
|
|
|
(provide see-button))
|
|
|
|
|
> (module b racket
|
|
|
|
|
(require 'a (for-syntax 'a))
|
|
|
|
|
(define-syntax (m stx)
|
|
|
|
|
see-button)
|
|
|
|
|
(m))
|
|
|
|
|
> (module b2 racket
|
|
|
|
|
(require 'a)
|
|
|
|
|
(eval see-button))
|
|
|
|
|
> (require 'b2)
|
|
|
|
|
button: undefined;
|
|
|
|
|
cannot reference an identifier before its definition
|
|
|
|
|
in module: top-level
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Mismatches like the one above can also arise when a macro tries to match
|
|
|
|
|
literal bindings—using `syntax-case` or `syntax-parse`.
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (module x racket
|
|
|
|
|
(require (for-syntax syntax/parse)
|
|
|
|
|
(for-template racket/base))
|
|
|
|
|
|
|
|
|
|
(provide (all-defined-out))
|
|
|
|
|
|
|
|
|
|
(define button 0)
|
|
|
|
|
(define (make) #'button)
|
|
|
|
|
(define-syntax (process stx)
|
|
|
|
|
(define-literal-set locals (button))
|
|
|
|
|
(syntax-parse stx
|
|
|
|
|
[(_ (n (~literal button))) #'#''ok])))
|
|
|
|
|
> (module y racket
|
|
|
|
|
(require (for-meta 1 'x)
|
|
|
|
|
(for-meta 2 'x racket/base))
|
|
|
|
|
|
|
|
|
|
(begin-for-syntax
|
|
|
|
|
(define-syntax (m stx)
|
|
|
|
|
(with-syntax ([out (make)])
|
|
|
|
|
#'(process (0 out)))))
|
|
|
|
|
|
|
|
|
|
(define-syntax (p stx)
|
|
|
|
|
(m))
|
|
|
|
|
|
|
|
|
|
(p))
|
|
|
|
|
eval:2.0: process: expected the identifier `button'
|
|
|
|
|
at: button
|
|
|
|
|
in: (process (0 button))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
In this example, `make` is being used in `y` at phase level 2, and it
|
|
|
|
|
returns the `#'button` syntax object—which refers to `button` bound at
|
|
|
|
|
phase level 0 inside `x` and at phase level 2 in `y` from `(for-meta 2
|
|
|
|
|
'x)`. The `process` macro is imported at phase level 1 from `(for-meta
|
|
|
|
|
1 'x)`, and it knows that `button` should be bound at phase level 1.
|
|
|
|
|
When the `syntax-parse` is executed inside `process`, it is looking for
|
|
|
|
|
`button` bound at phase level 1 but it sees only a phase level 2 binding
|
|
|
|
|
and doesn’t match.
|
|
|
|
|
|
|
|
|
|
To fix the example, we can provide `make` at phase level 1 relative to
|
|
|
|
|
`x`, and then we import it at phase level 1 in `y`:
|
|
|
|
|
|
|
|
|
|
```racket
|
|
|
|
|
> (module x racket
|
|
|
|
|
(require (for-syntax syntax/parse)
|
|
|
|
|
(for-template racket/base))
|
|
|
|
|
|
|
|
|
|
(provide (all-defined-out))
|
|
|
|
|
|
|
|
|
|
(define button 0)
|
|
|
|
|
|
|
|
|
|
(provide (for-syntax make))
|
|
|
|
|
(define-for-syntax (make) #'button)
|
|
|
|
|
(define-syntax (process stx)
|
|
|
|
|
(define-literal-set locals (button))
|
|
|
|
|
(syntax-parse stx
|
|
|
|
|
[(_ (n (~literal button))) #'#''ok])))
|
|
|
|
|
> (module y racket
|
|
|
|
|
(require (for-meta 1 'x)
|
|
|
|
|
(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))
|
|
|
|
|
> (require 'y)
|
|
|
|
|
'ok
|
|
|
|
|
```
|