[R6RS] Internal DEFINE vs. macros

Michael Sperber sperber
Sat Apr 9 09:08:00 EDT 2005

In the context of the module discussion, Richard and I have been
thinking about the interaction between internal DEFINE and macros.
(Parts of this email, specifically the examples, were written by
Richard.)  This interaction creates ambiguities in the language.
While these ambiguities already exist in R5RS, they become more
pronounced with the introduction of internal DEFINE-SYNTAX.  (I would
especially appreciate some help from Kent and Matthew who've both
implemented internal DEFINE-SYNTAX.)

I'll get back to a pure R5RS example later.  Here's a fairly
straightforward one:

 (let-syntax ((bar (syntax-rules ()
                     ((bar) 'outer))))
   (define first (lambda () (bar)))
   (define-syntax bar
     (syntax-rules ()
       ((bar) 'inner)))
   (define second (lambda () (bar)))
   (list (first) (second)))

It's not immediately obvious what definitions the two invocations of
BAR refer to---there's at least three possibilities.  PLT and Chez,
which offer internal DEFINE-SYNTAX, differ:

  => (inner inner)   ; PLT
  => (outer outer)   ; Chez

Before trying the example, I personally had expected behavior
analogous to the top level (whatever this meant in my confused mind),
i.e. (outer inner).

It's possible to come up with examples where there is no clear analogy
with the top level, and it becomes difficult to formulate a simple
explanation for the meaning of expansion.  (Let me know if you want to
see them---I didn't want to bloat this email even further.)

Ambiguities occur specifically when macros expand into internal
definitions, because macro expansion is sensitive to the current
environment but can also modify it.  This already occurs in R5RS,
albeit arguably in more obscure cases:

 (let ((bar 'outer))
   (let-syntax ((foo (syntax-rules (bar)
                       ((foo bar baz) baz)
                       ((foo x baz) (define x baz)))))
     (foo bar 'inner)

The two expansions of (foo bar 'inner),
   (define bar 'inner)
are both consistent.

The implementations I've tried all use the first, but it seems R5RS
allows both interpretations.  Should a macro expander first expand all
of the internal DEFINEs to see what the environment is and then go
back and re-expand them?  This wouldn't affect this example, but it's
easy to come up with ones where it would.

The problem is (almost) specific to local environments, and seems to
go hand-in-hand with shadowing.  (It is possible to construct
ambiguous examples using the top level only and a typical module
system, but they get even more obscure.)

The ambiguities are a problem in itself, but they also seem to make it
difficult to design the language in such a way that you can just paste
a block of top-level code into a local environment and expect it to
work the same way.  (I started of thinking this is merely a technical
problem, but, after being unsuccessful at finding a solution, am now
convinced that it's quite intrinsic.)

So I have several questions:

- Is it important to precisely define the semantics of macro
  expansion?  (I would say yes---people run across ambiguities in R5RS
  macros.  Manuel has just pointed out that others lurk in other
  corners of the hygienic macro system.)

- Kent and Matthew: can you offer suggestions on how to specify the
  semantics of internal DEFINE-SYNTAX?  Is there a simple way to do

- How far should we make the top level and local environments
  analogous?  In the abstract, it seems a desirable goal to make them
  as analogous as possible, but to me, it also seems quite difficult
  and complicated.  If they don't work the same way, the more similar
  they look, the harder it becomes to explain the differences.

Cheers =8-} Mike
Friede, V?lkerverst?ndigung und ?berhaupt blabla

More information about the R6RS mailing list