[R6RS] Eval vs. phases

dyb at cs.indiana.edu dyb at cs.indiana.edu
Sat Jul 29 14:56:53 EDT 2006


> OK, first really naive question about this.  Why doesn't
>
> <import-spec> --> <import-set>
>                 | (for <import-set> <import-phase>*)
>                 | (for-eval <import-set> <import-phase>*)
>
> cut the mustard?
>
> This would mean that the stuff in <import-set> in a `for-eval' spec is
> invoked/visited according to the same rules as stuff in a `for' spec,
> and the code passed to `eval' is evaluated as if it were sitting in
> the body of a module the (the-library-environment) is sitting in,
> where the `for-eval' imports apply rather than the `for' imports.

Thanks for getting the ball rolling.

With Model 2 (completely separate phases), we need two things out of the
syntax:

  * (Independent of eval) A way to specify in the library header imports
    for meta levels beyond expand.

  * A way to specify import phases for eval.

What you propose doesn't directly address the former, but I believe that
this can be fixed with an extension of <import-phase>, which perhaps you
are assuming.  I had earlier suggested:

  <import-phase> = run | expand | (meta n)

  where run is an abbreviation for (meta 0) and expand is an
  abbreviation of (meta 1).

As I recall, Matthew didn't like this syntax, but I'm not sure what he
would prefer.

What you propose does address the latter, but not sufficiently.  One
problem is that transformers can also call eval, and I don't think your
syntax reflects that possibility.

A deeper problem is that no fixed specification of import phases will
work, whether in the library header or elsewhere, since programs can
dynamically construct an evaluated form that is nested arbitrarily many
meta levels deep.  Similarly, programs can arbitrarily deeply nest eval
calls within evaluated expressions.

I've concluded from this that eval import specifications generally must be
constructed dynamically, with Model 2, to parallel the structure of the
evaluated form.  It might make sense to include import specifications as a
wrapper on the evaluated form, something like:

  <eval argument> -> (with-import (<import-spec>*) <expression>)

Or we could revert back to an earlier stab at environments to maintain
backward compatability with the (eval expr env) interface:

  (environment <import-spec>*)

except that environment would be a procedure rather than a syntactic form,
and each <import-spec> would be represented as an s-expression.  I assume
with the second option that the implied run-time loading/invocation of a
library at meta-level N would occur when the environment is created rather
than at some later time when/if eval is passed the environment.

On the other hand, we do need something in the library header as well, to
declare the set of libraries (or perhaps library bindings) that can be
used by eval so that a system knows the set of libraries for which both
compile-time and run-time information must be available at run time.  This
would be a bit of a wart and redundant as far as the programmer is
concerned, since this information is implicit in the import
specifications, but can't be helped since import specifications aren't
generally known until run time.

> Alternatively, we might do away with mucking with the library syntax
> entirely and just have `the-library-environment' evaluate to an
> environment corresponding exactly to the library it's sitting in.
> This might entail having to create a separate library sometimes.

I believe that this would force the system to load both compile-time and
run-time information for all imported libraries both at compile time and
run time, since any call to eval within a transformer or run-time code
could potentially use any imported binding.

> I suppose that leaves open the question of what happens to the
> libraries at levels >= 1 that have been invoked as part of visiting
> the one we're in.  I'm assuming these get re-invoked as part of
> expanding the eval'ed expression.  (I'm not sure I understand the
> semantics of invocation correctly---with re-invoke, I mean that the
> (instances of the?) newly invoked libraries don't share state with the
> (instances of the?) same libraries invoked at run time.

I assume that run-time code and (meta-level 0) code evaluated via a call
to eval from run-time code would share the same library bindings.  A call
to eval might, however, cause a library to be loaded or invoked if there's
a library used only for eval or if a library is used at something other
than meta-level 0 by the eval'd code.

Here are some other interesting questions:

 * Is a library invoked just once at meta-level N no matter how many times
   and from where eval is called at run time?

   Yes, I think.

 * If meta-level N code calls eval to evaluate an expression x, is x
   treated as if it too were at meta-level N?

   Yes, I think.  This would probably require a level variable to be bound
   dynamically by the expander and referenced by eval.

Kent



More information about the R6RS mailing list