[plt-scheme] Re: how many times macros are defined?

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Fri May 1 09:32:18 EDT 2009

At Fri, 1 May 2009 05:54:01 -0700 (PDT), Michele Simionato wrote:
> BTW, I have another question. In future Adventures I want to explain
> the difference between systems with implicit phasing
> (like Ikarus) and systems with explicit phasing (like PLT and
> Larceny).
> 
> The difference - as you know better than me - is that systems like PLT
> andLarceny can import names into a specific phase, whereas
> Ikarus necessarily imports the names into all phases.
> In principle Larceny and PLT are more powerful since they
> give you more control; in practice however I am having trouble
> in finding real life use cases where to import names into a
> specific phase only is necessary. For instance, I wrote this,
> but it seems to me a pretty weak use case:
> 
> """
> Suppose for instance you are a teacher, and you want to force
> your students to write their macros using only a functional
> subset of Scheme.
> You could then import at compile time all R6RS procedures
> except the nonfunctional ones (like ``set!``) while importing
> at runtime the whole of R6RS.
> """
> 
> Do you have more practical use case to suggest?

An actual variant of this example is the R5RS language, which imports
only `syntax-rules' for transformers and does not import `syntax-rules'
for run time.

The restriction of `define-syntax' to `syntax-rules' could also be
accomplished by providing a `define-syntax' so that builds in the
`syntax-rules' form. Our current strategy is somewhat simpler, and it's
certainly more extensible (e.g., using the `#%require', you can import
more transformer forms into a module that is otherwise in R5RS).

In most code, importing the same name with different bindings in
different phases is rare, though, and I think it's not a strong
motivation for separating phases.


In documentation, we very often exploit the ability to import a name
with a different binding in the "label" phase (example code that refers
to libraries) compared to the run-time phase (the code that implements
the document itself). Since other systems do not approach documentation
in the same way as PLT Scheme, though, that example also provides
little motivation to others for phase-specific bindings.


> There is yet another thing that troubles me.
> The R6RS document forbids the same name
> to be used with different bindings in different phases.
> Therefore, if I import the name 'x' at expand time (just to make
> an example) I am basically reserving the name 'x' for all phases,
> since I cannot reuse 'x' in other phases, unless it has the same
> binding as the first 'x'. In other words, the namespaces in the
> different phases are not really separated, and thus I could as well
> import the name 'x' in all phases, since I cannot re-use it with
> another meaning anyway.

That was a compromise design to allow both Ikarus-style and PLT-style
macro systems.


In PLT Scheme, the motivation for phase-specific bindings is not so
much that we may want different names in different phases, but that we
want to be specific about which code is running at which times. Without
an enforced separation, we found that it was far too easy to
accidentally have the run-time code depend on some initialization in
compile-time code (e.g. loading a module, initializing some state), and
that possibility made our builds and libraries fragile. (This problem
is described more in the "Composable and Compilable Macros" paper.) So,
we moved to a system that would tell us when we got it wrong, in much
the same way that the compiler complains when a module contains a free
variable.

There's a spectrum in language design on how much the compiler checks
statically versus how much is checked at run time, and static checks
inevitably rule out some programs that could have run just fine. On the
particular point of phase bindings, we've been happier with explicit
declarations and static checking. I think this choice has been key to
helping us build towers of languages that are far wider (lots of
languages) and deeper (languages built on other languages) than
anything else out there, that involve many implementors, and that we
can keep running over many years.



Posted on the users mailing list.