[plt-scheme] eval and phases (v4.0.1.2)
As of version 4.0.1.2 (now in SVN):
1. Each namespace has a "base phase" that is used by `eval',
`dynamic-require', etc., instead of always starting in phase 0.
2. While compile-time expressions are evaluated (such as the body of a
macro transformer or the right-hand side of `define-for-syntax'),
the current namespace is set to one whose base phase matches the
evaluation phase.
For example, running
#lang scheme/load
(define x 5)
(define-for-syntax x 8)
(begin-for-syntax
(printf "~s\n" (eval 'x)))
now produces the output 8 instead of 5, since the expansion-time call
to `eval' uses a namespace whose base phase is 1.
This change fixes two specific problems:
* Using deserialization at macro-expansion time didn't work before, as
Danny noticed:
http://list.cs.brown.edu/pipermail/plt-scheme/2008-February/022986.html
* Catching R6RS exceptions from uses of `eval' at macro expansion time
didn't work before, as Steve noticed:
http://list.cs.brown.edu/pipermail/plt-scheme/2008-May/024712.html
More generally, `eval' no longer skips across phases boundaries. Unless
you find anther route from one phase to another, if you're running in
phase N, you'll only see namespaces with base phase N.
[I know of two remaining ways to pass data across phases: via "3-D
syntax', where a value is wrapped in a syntax object and embedded in
an expression as a "constant", and via built-in parameters, such as a
module-name resolver. Closing those holes is future work.]
As usual, the `module' form plays a crucial role in keeping everything
coherent. Suppose that you're expanding module M, and a macro
transformer used by expansion needs to compile another module N. (This
happens all the time when a `require' form is expanded and N is not
already compiled to bytecode.) The current namespace for compiling N
starts at base phase 1. When the `module' form starts expanding the
body of N, however, it creates a new namespace that is back at phase 0.
Globally, this works out because phase 1 for expanding N is distinct
from other phase 1 namespaces; in particular, macro transformers
running to expand N don't contaminate the (temporarily suspended)
expansion of M. Locally, it means that you can reason about modules and
phases the same as before.
[If you're writing a load handler for `module' forms (few people do
that), then you need to be careful to inject a `module' identifier
with a binding in right phase, since the load handle could be
triggered at an arbitrary phase. The new
`namespace-module-identifier' function helps get such an identifier.]
I don't expect this change to create much trouble, but let me know if
you run into behavior that looks like a bug. It wasn't especially
difficult to implement the change, but there are more opportunities
than usual for me to get something wrong or to overlook
modes/combinations in testing.
Matthew