[plt-scheme] let-syntax bug

From: Bradd W. Szonye (bradd+plt at szonye.com)
Date: Thu Apr 8 16:37:21 EDT 2004

> On Thu, 8 Apr 2004, Bradd W. Szonye wrote:
>> In short, I think your analogy leads to an attempt to "return" an
>> internal definition somehow, which is currently an error both for
>> normal expressions and for syntax. Furthermore, it's an error for a
>> good reason: permitting it would break scoping.

Andre van Tonder wrote:
> Yet the same argument could be made against
> (define-syntax foo
>   (syntax-rules ()
>     ((foo bar) (define-syntax bar
>                  (syntax-rules () 
>                    ((bar) 'hello))))))
> which returns a definition ....

This doesn't "return" anything, because SYNTAX-RULES, unlike LAMBDA,
does not introduce a scope. Once FOO is bound in the top level, its
region covers the whole program (except where it's shadowed). Because
FOO covers the top level, and because SYNTAX-RULES does not create a
scope, FOO can introduce new bindings into the top level.

> but is indeed valid Scheme, and which would be analogous to
> (define foo
>   (lambda ()
>     (begin 
>       (define bar ......)))))
> which attempts to return a defition and is invalid.  

The analogy is invalid, because LAMBDA, unlike SYNTAX-RULES, does
introduce a scope. This FOO also covers the top level, but the LAMBDA
does not. Therefore, the LAMBDA cannot introduce new bindings into the
top level; that would break scope.

Likewise, in

    (let-syntax foo ...
      (define-syntax bar ...))

LET-SYNTAX does not cover the top level, so it cannot bind BAR in the
top level. Again, that would break scope.

To avoid confusion, Scheme uses the same scoping rules and regions for
syntactic bindings and variable binding. Therefore,

    (let-syntax foo ...
      (define bar ...))

is also in error. However, this latter restriction is only a slight
inconvenience, because lexical closures are first-class values. You can
simply return closures from the let-syntax and then bind them in the top
level. There's no need to break scope, because you can create the object
inside the scope and then pass it out of the scope to bind it --
variables can't cross scopes, but objects can.

Unfortunately, you can't do the same thing with syntactic bindings,
because they aren't first-class values. Therefore, there's no similar
work-around for DEFINE-SYNTAX in R5RS. Some implementations do provide
first-class macros, or at least something close enough for this use.
Bradd W. Szonye

Posted on the users mailing list.