Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Question about R6RS let-syntax

12 views
Skip to first unread message

Derick Eddington

unread,
Oct 16, 2009, 10:44:10 PM10/16/09
to
What is the correct behavior for the following program?

(import (rnrs))
(define (W x) (write x) (newline))
(let-syntax ((foo (lambda (_) "outer")))
(define-syntax foo (lambda (_) "inner"))
(W (foo)))
(W (foo))


These Scheme systems don't all agree:

$ ikarus --r6rs-script let-syntax--inner-outer.sps
"outer"
"inner"
$
$ petite --program let-syntax--inner-outer.sps
Exception: definition not permitted (define-syntax foo (lambda (_)
"inner")) at line 5, char 3 of let-syntax--inner-outer.sps
Type (debug) to enter the debugger.
$
$ larceny --r6rs --program let-syntax--inner-outer.sps
"outer"
"inner"
$
$ plt-r6rs let-syntax--inner-outer.sps
let-syntax--inner-outer.sps:5:17: define-syntax: cannot define
imported identifier at: foo in: (define-syntax foo (lambda (_)
"inner"))

=== context ===
/home/d/plt/collects/rnrs/base-6.ss:525:0

$
$ ypsilon --r6rs let-syntax--inner-outer.sps
"inner"
"inner"
$


Thanks,

--
: Derick
----------------------------------------------------------------

jhaase

unread,
Oct 17, 2009, 6:40:08 AM10/17/09
to
R6RS 11.18 (p.56):

"Like a begin form, a let-syntax or letrec-syntax form
may appear in a definition context, in which case it is
treated as a definition, and the forms in the body must
also be definitions. A let-syntax or letrec-syntax form
may also appear in an expression context, in which case
the forms within their bodies must be expressions."

My understandig of this paragraph is, that something like

(let-syntax (---)
<definition>
<expression>)

is already illegal, because definitions and expressions are mixed in
the body of the form. This means, Ikarus, Larceny and Ypsilon are
wrong.

So lets explore Chez and PLT further (because I don't understand their
error messages 100%) and change the program to:

(import (rnrs))
(define (W x) (write x) (newline))
(let-syntax ((foo (lambda (_) "outer")))

(define-syntax bar (lambda (_) "inner"))
(W (foo)))
(W (bar))

According to the cited Paragraph this should still be illegal, because
of the mix of definitions and expressions.

juergen@Nix:~$ petite --program t1.ss
"outer"
"inner"
juergen@Nix:~$ plt-r6rs t1.ss
"outer"
"inner"

Both splice the body of the let-syntax into the surrounding context
without giving an error. So it seems indeed all implementations are
buggy ;)

Please, someone correct me, if I interpreted the cited paragraph
wrongly.

Grettings,
Juergen

Marco Maggi

unread,
Oct 17, 2009, 9:17:53 AM10/17/09
to
"jhaase" wrote:
> R6RS 11.18 (p.56):
>
> "Like a begin form, a let-syntax or letrec-syntax form may
> appear in a definition context, in which case it is
> treated as a definition, and the forms in the body must
> also be definitions. A let-syntax or letrec-syntax form
> may also appear in an expression context, in which case
> the forms within their bodies must be expressions."
>
> My understandig of this paragraph is, that something like
>
> (let-syntax (---)
> <definition>
> <expression>)
>
> is already illegal, because definitions and expressions
> are mixed in the body of the form. This means, Ikarus,
> Larceny and Ypsilon are wrong.

The following works under Ikarus, Larceny, Mosh, Ypsilon and
Petite Chez Scheme:

(import (rnrs))
(let ((a 1))
(let-syntax ((b (identifier-syntax 3)))
(define c 3)
(write (list a b c))))
(newline)

and it is correct according to my understanding of[1]:

The <form>s of a let-syntax form are treated, whether in
definition or expression context, as if wrapped in an
implicit begin; see section 11.4.7. Thus definitions in
the result of expanding the <form>s have the same region
as any definition appearing in place of the let-syntax
form would have.

meaning that a <form> can be in definition context or
expression context, not let-syntax as a whole; my opinion is
that there is no explicit note about switching from
<definition> to <expression> contexts inside let-syntax,
because it was perceived as "redundant" by the editors.

[1] <http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_sec_11.18>
--
Marco Maggi

Derick Eddington

unread,
Oct 17, 2009, 11:27:38 AM10/17/09
to
On Oct 17, 3:40 am, jhaase <juergen.m.ha...@googlemail.com> wrote:
> R6RS 11.18 (p.56):
>
> "Like a begin form, a let-syntax or letrec-syntax form
> may appear in a definition context, in which case it is
> treated as a definition, and the forms in the body must
> also be definitions. A let-syntax or letrec-syntax form
> may also appear in an expression context, in which case
> the forms within their bodies must be expressions."
>
> My understandig of this paragraph is, that something like
>
> (let-syntax (---)
> <definition>
> <expression>)
>
> is already illegal, because definitions and expressions are mixed in
> the body of the form.


On Oct 17, 6:17 am, Marco Maggi <mrc....@gmail.com> wrote:
> and it is correct according to my understanding of[1]:
>
> The <form>s of a let-syntax form are treated, whether in
> definition or expression context, as if wrapped in an
> implicit begin; see section 11.4.7. Thus definitions in
> the result of expanding the <form>s have the same region
> as any definition appearing in place of the let-syntax
> form would have.
>
> meaning that a <form> can be in definition context or
> expression context, not let-syntax as a whole; my opinion is
> that there is no explicit note about switching from
> <definition> to <expression> contexts inside let-syntax,
> because it was perceived as "redundant" by the editors.
>
> [1] <http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_sec_11.18>


Both those cited paragraphs say the let-syntax <form>s are spliced
like for begin. So let's review begin.

R6RS 10:

begin form:
The expander splices the subforms into the
list of body forms it is processing. (See section 11.4.7.)

R6RS 11.3:

When begin, let-syntax, or letrec-syntax forms occur
in a body prior to the first expression, they are spliced
into the body; see section 11.4.7.

R6RS 11.4.7:

(begin <form> ...) syntax
(begin <expression> <expression> ...) syntax

The <begin> keyword has two different roles, depending on
its context:

* It may appear as a form in a <body> (see section 11.3),
<library body> (see section 7.1), or <top-level body>
(see chapter 8), or directly nested in a begin form that
appears in a body. In this case, the begin form must
have the shape specified in the first header line. This
use of begin acts as a splicing form -- the forms inside
the <body> are spliced into the surrounding body, as
if the begin wrapper were not actually present.

A begin form in a <body> or <library body> must be
non-empty if it appears after the first <expression>
within the body.

* It may appear as an ordinary expression [...]


I think the part of 11.4.7, "the forms inside the <body> are spliced
into the surrounding body", is a mistake and should instead be: "the
forms inside the begin are spliced into the surrounding body".

I think all the above implies that if a let-syntax in a body has
definition and expression forms, those forms are "spliced into the
surrounding body, as if the [let-syntax] wrapper were not actually
present".

--
: Derick
----------------------------------------------------------------

Derick Eddington

unread,
Oct 17, 2009, 1:08:11 PM10/17/09
to
On Oct 17, 8:27 am, Derick Eddington <derick.edding...@gmail.com>
wrote:

> I think all the above implies that if a let-syntax in a body has
> definition and expression forms, those forms are "spliced into the
> surrounding body, as if the [let-syntax] wrapper were not actually
> present".

Now the question is: what does the foo in the first (foo) refer to?

(import (rnrs))
(define (W x) (write x) (newline))
(let-syntax ((foo (lambda (_) "outer")))
(define-syntax foo (lambda (_) "inner"))
(W (foo)))
(W (foo))

R6RS 11.18 says of let-syntax:

Semantics: The <form>s are expanded in the syntactic
environment obtained by extending the syntactic environment
of the let-syntax form with macros whose keywords are the
<keyword>s, bound to the specified transformers. Each
binding of a <keyword> has the <form>s as its region.

Does that mean?:
The (define-syntax foo ---) is expanded into some implementation-
dependent core syntactic keyword binding form of a foo identifier with
lexical context in scope of the let-syntax's foo, and the first (foo)
is expanded with its foo referring to the let-syntax's. The expanded
result is spliced into the position of the let-syntax, and the define-
syntax'ed foo captures the foo in the second (foo) because the lexical
context of the define-syntax'ed foo includes the scope outside the let-
syntax. If this is the correct interpretation, then Ikarus and
Larceny are correct.

Or does that mean?:
The syntactic environment of the let-syntax, i.e. the top-level
environment, is extended with a binding of foo to the "outer" macro,
and so the (define-syntax foo ---) conflicts with the already-bound
foo because it's attempting to bind another foo in the same
environment, i.e. the let-syntax-extended top-level environment. If
this is the correct interpretation, then Chez and PLT seem correct,
though I question whether their errors are because they actually did
interpret the situation as just described.

Or does that mean?:
The syntactic environment of the let-syntax is known to be in a body
after a definition. Regardless of the fact that environment is
extended by the let-syntax's foo, and because it is known that
environment is in a body after a definition, and because let-syntax
forms are treated "as if wrapped in an implicit begin" (per R6RS
11.18), when expanding the (define-syntax foo ---) it binds its foo in
the top-level body, and when expanding the first (foo) it is known to
be in the body where the define-syntax'ed foo was just bound and so it
refers to that foo. If this is the correct interpretation, then
Ypsilon is correct.

Good luck, experts... :)

--
: Derick
----------------------------------------------------------------

Aaron W. Hsu

unread,
Oct 17, 2009, 3:12:42 PM10/17/09
to jhaase
On Sat, 17 Oct 2009 06:40:08 -0400, jhaase
<juergen...@googlemail.com> wrote:

> Please, someone correct me, if I interpreted the cited paragraph
> wrongly.

I thought about this as well, but the problem with expecting only
definitions or only expressions when it is in either a definition context
or expression context is that we aren't dealing with just this sort of
context in a top level program. The top-level semantics allow mixed
definitions and expressions, so are we really in either an expression or a
definition context? I don't think so. I think the intent is to have
LET-SYNTAX act like a splicing syntax in the same way as BEGIN works,
which means that the boundary between definitions and expressions may
legally occur in a LET-SYNTAX form. On the other hand, this does not help
to explain why these errors occur. Chez Scheme usually provided a
different error messages for duplicate definitions, which is what I might
have expected, but this does not seem to be the same error message, so I
don't know what to think about this, yet.

Aaron W. Hsu

--
Of all tyrannies, a tyranny sincerely exercised for the good of its
victims may be the most oppressive. -- C. S. Lewis

Abdulaziz Ghuloum

unread,
Oct 17, 2009, 5:58:37 PM10/17/09
to
On Oct 17, 5:44 am, Derick Eddington <derick.edding...@gmail.com>
wrote:

> What is the correct behavior for the following program?
>
> (import (rnrs))
> (define (W x) (write x) (newline))
> (let-syntax ((foo (lambda (_) "outer")))
>   (define-syntax foo (lambda (_) "inner"))
>   (W (foo)))
> (W (foo))

Here's my rationalization for Ikarus's behavior.

1. The identifiers bound by let-syntax shadow outer bindings. E.g.,
(let ((x 11))
(define x 12)
(let-syntax ((x (identifier-syntax 13)))
x))
=> 13
This is basic and no one would argue against it (I hope).
Note that shadowing and scope only affect references (or uses) to the
bound identifiers. So, the lexical scope of the body of the let-
syntax
has a chain of 3 'x's, regardless of what the body actually looks
like.

2. Let-syntax does not establish a new scoping contour. Meaning that
definitions inside of the let-syntax do not shadow the let-syntax
bound
keywords, instead, they are added to the outer contour into which the
let-syntax is spliced.

Now what you're confused about is evident in your choice of strings in
your example. The let-syntax is actually "inner" to the "outer"
context.
So, let me fix that. Here's a slightly modified example:

(let ()


(define (W x) (write x) (newline))

(define-syntax foo (lambda (_) "outer"))
(let-syntax ((foo (lambda (_) "inner")))
(W (foo)))
(W (foo)))
=> inner, outer

[Do you expect the above example to behave differently?]

Now we can push the outer define-syntax inside the let-syntax without
changing anything because (1) the let-syntax is going to splice it
back
again, and (2) right-hand-side of the define-syntax does not reference
foo and won't be affected by the shadowing.

(let ()


(define (W x) (write x) (newline))

(let-syntax ((foo (lambda (_) "inner")))
(define-syntax foo (lambda (_) "outer"))
(W (foo)))
(W (foo)))
=> inner, outer

Does this make sense? [you probably need to let it rest a little
first]

Aziz,,,

Derick Eddington

unread,
Oct 17, 2009, 8:41:12 PM10/17/09
to
On Oct 17, 2:58 pm, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:
> Now what you're confused about is evident in your choice of strings in
> your example. The let-syntax is actually "inner" to the "outer"
> context.

(I did wonder about that. I left the "outer" and "inner" as I did in
order to show that it's potentially confusing.)

> So, let me fix that. Here's a slightly modified example:
>
> (let ()
> (define (W x) (write x) (newline))
> (define-syntax foo (lambda (_) "outer"))
> (let-syntax ((foo (lambda (_) "inner")))
> (W (foo)))
> (W (foo)))
> => inner, outer
>
> [Do you expect the above example to behave differently?]

Definitely not.

> Now we can push the outer define-syntax inside the let-syntax without
> changing anything because (1) the let-syntax is going to splice it
> back
> again, and (2) right-hand-side of the define-syntax does not reference
> foo and won't be affected by the shadowing.
>
> (let ()
> (define (W x) (write x) (newline))
> (let-syntax ((foo (lambda (_) "inner")))
> (define-syntax foo (lambda (_) "outer"))
> (W (foo)))
> (W (foo)))
> => inner, outer
>
> Does this make sense? [you probably need to let it rest a little
> first]

Yes. Justifying Ikarus's and Larceny's behavior based on the
equivalence of pushing an outside (define-syntax foo ---) into the let-
syntax makes sense. (But see my next message for a related new
question.)

Ikarus's and Larceny's behavior initially made the most sense to me,
based on similar reasoning to your explanation. But after thinking
about why the other systems behave differently, I thought the language
in R6RS might be plausibly interpreted in the three different ways I
described in my previous message.

--
: Derick
----------------------------------------------------------------

Derick Eddington

unread,
Oct 17, 2009, 8:44:25 PM10/17/09
to
On Oct 17, 2:58 pm, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:
> (let ()
> (define (W x) (write x) (newline))
> (define-syntax foo (lambda (_) "outer"))
> (let-syntax ((foo (lambda (_) "inner")))
> (W (foo)))
> (W (foo)))
> => inner, outer
>
> [Do you expect the above example to behave differently?]
>
> Now we can push the outer define-syntax inside the let-syntax without
> changing anything because (1) the let-syntax is going to splice it
> back
> again, and (2) right-hand-side of the define-syntax does not reference
> foo and won't be affected by the shadowing.
>
> (let ()
> (define (W x) (write x) (newline))
> (let-syntax ((foo (lambda (_) "inner")))
> (define-syntax foo (lambda (_) "outer"))
> (W (foo)))
> (W (foo)))
> => inner, outer
>
> Does this make sense?

I tried making the right-hand-side of the define-syntax reference foo.

What is the correct behavior for the following program?

(import (rnrs))
(define (W x) (write x) (newline))

(let-syntax ((foo (lambda (_) "A")))
(define-syntax foo
(syntax-rules ()
((_) (foo "B"))
((_ x) x)))
(W (foo)))
(W (foo))

These Scheme systems don't all agree:

$ ikarus --r6rs-script let-syntax--shadowing.sps
"A"
"A"
$
$ petite --program let-syntax--shadowing.sps
Exception: definition not permitted (define-syntax foo (syntax-rules
() ((...) (...)) ((...) x))) at line 5, char 3 of let-syntax--
shadowing.sps


Type (debug) to enter the debugger.
$

$ larceny --r6rs --program let-syntax--shadowing.sps
"A"
"A"
$
$ plt-r6rs let-syntax--shadowing.sps
let-syntax--shadowing.sps:5:17: define-syntax: cannot define imported
identifier at: foo in: (define-syntax foo (syntax-rules () ((_) (foo
"B")) ((_ x) x)))

=== context ===
/home/d/plt/collects/rnrs/base-6.ss:525:0

$
$ ypsilon --r6rs let-syntax--shadowing.sps
"B"
"B"
$


The only seemingly relevant citations I could find are:

R6RS 11.2.2:

All bindings established by a set of definitions, whether
keyword or variable definitions, are visible within the
definitions themselves.

R6RS 11.3:

Each identifier defined by a definition is local to the
<body>. That is, the identifier is bound, and the region of
the binding is the entire <body> (see section 5.2).

I'm not sure how to apply these two citations to determine the correct
behavior.

Even though the let-syntax's foo is in scope around the define-syntax
form, don't both the foo immediately next to the define-syntax and the
foo in the syntax-rules clause have matching lexical context info
(marks, anti-marks, or whatever)? So, when the define-syntax form is
spliced into the body, the foo in the syntax-rules clause should refer
to the define-syntax'ed foo because their lexical context info
matches?

If, instead, the foo in the syntax-rules clause refers to the let-
syntax's foo, then isn't the expansion of the second (foo) referring
to the let-syntax's foo out of context? [1]

[1] https://bugs.launchpad.net/ikarus/+bug/309223

--
: Derick
----------------------------------------------------------------

Aaron W. Hsu

unread,
Oct 17, 2009, 9:55:19 PM10/17/09
to
On Fri, 16 Oct 2009 22:44:10 -0400, Derick Eddington
<derick.e...@gmail.com> wrote:

> What is the correct behavior for the following program?
>
> (import (rnrs))
> (define (W x) (write x) (newline))
> (let-syntax ((foo (lambda (_) "outer")))
> (define-syntax foo (lambda (_) "inner"))
> (W (foo)))
> (W (foo))

[...]

> $ petite --program let-syntax--inner-outer.sps
> Exception: definition not permitted (define-syntax foo (lambda (_)
> "inner")) at line 5, char 3 of let-syntax--inner-outer.sps
> Type (debug) to enter the debugger.

I have been trying to figure out whether this is a correct or incorrect
behavior of Chez Scheme. The important questions are how definitions of
keywords are treated in the expansion process, and how we expand the body
of a LET-SYNTAX form.

The latter question lends itself to an easier answer, however, and one
which I believe makes the Ikarus and Larceny behavior incorrect. According
to the R6RS semantics of LET-SYNTAX, it says that "[t]he [body is]

expanded in the syntactic environment obtained by extending the syntactic
environment of the let-syntax form with macros whose keywords are the

<keyword>s, bound to the specified transformers." Notice that it says the
body is *expanded*. In the R6RS, this term is defined and used as defined
in the Expansion Process Chapter 10. That is, during expansion, if we
encounter a LET-SYNTAX form, we have a starting syntactic environment, and
we have the set of bindings specified by the <bindings> form of
LET-SYNTAX. We first extend the syntactic environment with the bindings
specified, and only after this is done, do we continue with the expansion
of the spliced forms.

In the above example, the next form to be expanded would be the
DEFINE-SYNTAX form. Assuming that this is legal, the only reasonable
reading of the R6RS I can imagine dictates that the DEFINE-SYNTAX forms
alter the current lexical environment. Thus, when the DEFINE-SYNTAX form
is completed, the next form, the (W (foo)) form is expanded in a syntactic
environment wherein the "foo" identifier, if anything, can only reference
the "inner" foo macro. That is, I don't think that somehow the forms of
the LET-SYNTAX body are somehow expanded without any consideration to
those forms. That doesn't makes sense from reading the R6RS.

On the other hand, this reading leads me into a bit of trouble, because
when expanding, you can't define multiple keywords in the same body
expansion. If you were to define two foos in the same LET form, for
example, Chez would give you an error that you have defined multiple
instances of the keyword foo. The syntactic environment is the same one
throughout, and I don't think any definition during expansion is able to
alter or change a binding already defined. That is, DEFINE and
DEFINE-SYNTAX are not SET!.

Given this, it makes sense that PLT Scheme and Chez Scheme would signal
errors, but what doesn't make sense is the different error. Chez Scheme
seems to distinguish this case from the case of multiple definitions. I
wonder if this is intentional for clarity's sake, or whether it is a
pragmatic decision from the code paths.

jhaase

unread,
Oct 18, 2009, 6:41:57 AM10/18/09
to
On Oct 17, 9:12 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:
> On Sat, 17 Oct 2009 06:40:08 -0400, jhaase  
>
> <juergen.m.ha...@googlemail.com> wrote:
> > Please, someone correct me, if I interpreted the cited paragraph
> > wrongly.
>
> I thought about this as well, but the problem with expecting only  
> definitions or only expressions when it is in either a definition context  
> or expression context is that we aren't dealing with just this sort of  
> context in a top level program. The top-level semantics allow mixed  
> definitions and expressions, so are we really in either an expression or a  
> definition context? I don't think so. ...

I don't see a problem in this for top-level programs. It seems to be
quite easy to me: If you have only definitions inside a let-syntax,
the let-syntax itself counts as a "definition", so you can put it
anywhere inside the top-level. If you have only expressions, then it
counts as an expression and you can still put it anywhere inside the
top-level.

I'm no native english speaker, but the wording of the cited paragraph
seems quite clear to me. Either one or the other, not both. And when
you read the ongoing discussion between Derrick and Aziz, it becomes
even more clearer to me, that it is intentional by the r6rs editors
that you're not allowed to mix both, because it could lead to strange
situations.

What does someone naively expect who writes the following?

(let-syntax (---)
(define-syntax foo ---)
(display (foo)))

Probably that foo is local to the let-syntax, which is wrong, because
let-syntax doesn't create a local environment.

The correct way to write this is instead

(let-syntax (---)
(let ()
(define-syntax foo ---)
(display (foo))))

And this is enforced by the r6rs definition of let-syntax as I
understand it.

Now what can you expect if you write

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

Probably not that foo and bar are local, because it wouldn't make any
sense. And it is easy to see, that this could be put into any
definition context. The same goes for

(let-syntax (---)
(display 'foo)
(display 'bar))

This goes clearly into an expression context.

Maybe some r6rs editor might clear this up? (Mike where are you?^^)

Greetings,
Juergen

jhaase

unread,
Oct 18, 2009, 7:16:22 AM10/18/09
to
On Oct 18, 12:41 pm, jhaase <juergen.m.ha...@googlemail.com> wrote:
> ... more clearer ...

This is exactly what I meant with

> I'm no native english speaker

:-)

Greetings,
Juergen

Derick Eddington

unread,
Oct 18, 2009, 11:03:33 AM10/18/09
to
On Oct 18, 3:41 am, jhaase <juergen.m.ha...@googlemail.com> wrote:
> On Oct 17, 9:12 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:
>
> > On Sat, 17 Oct 2009 06:40:08 -0400, jhaase
>
> > <juergen.m.ha...@googlemail.com> wrote:
> > > Please, someone correct me, if I interpreted the cited paragraph
> > > wrongly.
>
> > I thought about this as well, but the problem with expecting only
> > definitions or only expressions when it is in either a definition context
> > or expression context is that we aren't dealing with just this sort of
> > context in a top level program. The top-level semantics allow mixed
> > definitions and expressions, so are we really in either an expression or a
> > definition context? I don't think so. ...
>
> I don't see a problem in this for top-level programs. It seems to be
> quite easy to me: If you have only definitions inside a let-syntax,
> the let-syntax itself counts as a "definition", so you can put it
> anywhere inside the top-level. If you have only expressions, then it
> counts as an expression and you can still put it anywhere inside the
> top-level.
>
> I'm no native english speaker, but the wording of the cited paragraph
> seems quite clear to me. Either one or the other, not both. And when
> you read the ongoing discussion between Derrick and Aziz, it becomes
> even more clearer to me, that it is intentional by the r6rs editors
> that you're not allowed to mix both, because it could lead to strange
> situations.

Here is a top-level program example where you want let-syntax to just
splice its expanded forms and not be restricted to only definitions or
expressions:

(import (rnrs))
(define (W x) (write x) (newline))

(define-syntax def-and-use
(syntax-rules ()
((_ name number)
(let-syntax ((helper (syntax-rules ()
((_ x)
(W (+ 1 (x) 2))))))
(define-syntax name
(syntax-rules () ((_) number)))
(helper name)))))
(def-and-use m 3)
(define var "a definition here")
(W (list (- (m)) var))

Also, in a local body, you might want to do:

(let ()
(def-and-use m 4)
(/ (m)))

I don't want let-syntax to be unnecessarily restricted. I think the
real problem is the unclear specification of let-syntax about the
corner case I discovered.

If let-syntax were restricted, things like def-and-use could be
accomplished by putting the expressions in dummy definitions.
However, the return values of the last expression could not be
returned (if that's something you wanted to be possible), i.e. (let ()
(def-and-return-use-vals ---)), which is admittedly unusual, but
should be possible in the spirit of making things fully general.

> What does someone naively expect who writes the following?
>
> (let-syntax (---)
> (define-syntax foo ---)
> (display (foo)))
>
> Probably that foo is local to the let-syntax, which is wrong, because
> let-syntax doesn't create a local environment.

It's probably true people will sometimes get confused by that. But
not having an inner body scope is a necessary quality of let-syntax,
and users of let-syntax should understand that. And being able to
splice a mix of definitions and expressions is necessary for full
generality.


On Oct 18, 4:16 am, jhaase <juergen.m.ha...@googlemail.com> wrote:
> On Oct 18, 12:41 pm, jhaase <juergen.m.ha...@googlemail.com> wrote:
>
> > ... more clearer ...
>
> This is exactly what I meant with
>

> > I'm no native english speaker
>

> :-)

No worries. I only speak one language. In my serfdom (U.S.A.) native
serfs make that mistake all the time. I would suck at speaking your
serfdom's language a lot more :)

P.S. You spelled my name wrong, too :)

--
: Derick
----------------------------------------------------------------

Aaron W. Hsu

unread,
Oct 18, 2009, 6:13:33 PM10/18/09
to
On Sat, 17 Oct 2009 17:58:37 -0400, Abdulaziz Ghuloum <aghu...@gmail.com>
wrote:

> Let-syntax does not establish a new scoping contour. Meaning that
> definitions inside of the let-syntax do not shadow the let-syntax
> bound
> keywords, instead, they are added to the outer contour into which the
> let-syntax is spliced.

'let-syntax' does not, indeed, establish any new contex, but according to
the R6RS documents, it actually expands its body in the same syntactic
environment as the environment of the 'let-syntax' form with the addition
that said environment is extended by the bindings specified by the
let-syntax form itself. To me, this means that any alteration of the
syntactic environment by forms inside the body of the 'let-syntax' must
also have the potential of trying to alter a binding already defined by
the 'let-syntax' form itself. That is, you don't do something special to
the body with the 'let-syntax' bindings in mind, and this splice it into
the previous syntactic environment in which the 'let-syntax' form occured,
which is what you appear to be saying. Instead, you alter the syntactic
environment in which the 'let-syntax' form occurs, and then splice the
forms of the 'let-syntax' body into that context and continue expanding as
normal, which results either in a shadowing of keywords defined in the
'let-syntax' body that were also defined in the 'let-syntax' binding
section, or, an error, since it is not possible to alter the binding of a
keyword in a single contour once it has been bound.

Am I missing something?

Brian Mastenbrook

unread,
Oct 19, 2009, 12:30:42 AM10/19/09
to

Aziz,

This is what this strategy leads to:

#!r6rs

(import (rnrs base (6))
(rnrs io simple (6)))

(define display/newline (lambda (e) (display e) (newline)))

(define-syntax my-begin (identifier-syntax begin))

(let-syntax ((foo (syntax-rules () ((_) 'outer))))
(my-begin
(define-syntax foo (syntax-rules () ((_) 'inner)))
(display/newline (foo))))

(let-syntax ((bar (syntax-rules () ((_) 'outer))))
(begin
(define-syntax bar (syntax-rules () ((_) 'inner)))
(display/newline (bar))))


$ ikarus --r6rs-script test.ss
inner
outer

I think this should always be an error. My mental model is that the
bindings established by a top-level `let-syntax' form are transformed
into top-level definitions of identifiers that are renamed throughout
the scope of the body. Here's an implementation of that idea:

(define-syntax sorta-toplevel-let-syntax
(lambda (stx)
(syntax-case stx ()
((_ ((name transformer) ...) body ...)
(for-all identifier? #'(name ...))
(letrec ((assert-nonduplicate-identifiers
(lambda (e)
(syntax-case e ()
(() #t)
((id) #t)
((id1 id2 others ...)
(begin
(when (bound-identifier=? #'id1 #'id2)
(syntax-violation #f "Duplicate bound
identifiers" stx #'id1))
(assert-nonduplicate-identifiers #'(id1
others ...))
(assert-nonduplicate-identifiers #'(id2
others ...))))))))
(assert-nonduplicate-identifiers #'(name ...))
(with-syntax (((temporaries ...) (generate-temporaries
#'(name ...))))
(letrec ((get-replacement (lambda (n)
(exists (lambda (pair)
(syntax-case pair ()
((name temp)
(bound-
identifier=? n #'name)
#'temp)
((name temp) #f)))
#'((name temporaries) ...))))
(replace-ids (lambda (stx)
(syntax-case stx ()
(n (and (identifier? #'n) (exists
(lambda (n^)

(bound-identifier=? #'n n^))

#'(name ...)))
(get-replacement #'n))
((a . b) (cons (replace-ids #'a)
(replace-ids
#'b)))
(z #'z)))))
(with-syntax ((new-body (replace-ids #'(begin
body ...))))
#'(begin
(define-syntax temporaries transformer) ...
new-body)))))))))

Do you have a convincing argument against this model?

Abdulaziz Ghuloum

unread,
Oct 19, 2009, 6:00:05 AM10/19/09
to
On Oct 18, 3:44 am, Derick Eddington <derick.edding...@gmail.com>
wrote:

> I tried making the right-hand-side of the define-syntax reference foo.


>
> What is the correct behavior for the following program?
>
> (import (rnrs))
> (define (W x) (write x) (newline))
> (let-syntax ((foo (lambda (_) "A")))
>   (define-syntax foo
>     (syntax-rules ()
>       ((_) (foo "B"))
>       ((_ x) x)))
>   (W (foo)))
> (W (foo))
>
> These Scheme systems don't all agree:

This is slightly more involved.

Let's look at the inner one first:

(let-syntax ((foo (lambda (_) "A")))
(define-syntax foo
(syntax-rules ()
((_) (foo "B"))
((_ x) x)))
(W (foo)))

The let-syntax-bound foo shadows the define-syntax-bound foo
because the define-syntax belongs to definitions outside of
the let-syntax even though it appears inside (and let-syntax
does not establish a new scope so, the define-syntax foo
cannot be inner to the let-syntax). So, let's rename the
let-syntax foo and all references to it preserving the
scope:

(let-syntax ((foo1 (lambda (_) "A")))
(define-syntax foo
(syntax-rules ()
((_) (foo1 "B"))
((_ x) x)))
(W (foo1)))

This obviously expands to (W "A").


Now let's look at the other part and forget about the first:

(let-syntax ((foo (lambda (_) "A")))
(define-syntax foo
(syntax-rules ()
((_) (foo "B"))

((_ x) x))))
(W (foo))

After doing the renaming, you get:

(let-syntax ((foo1 (lambda (_) "A")))
(define-syntax foo
(syntax-rules ()
((_) (foo1 "B"))
((_ x) x))))
(W (foo))

and this would be equivalent to:

(define-syntax foo
(syntax-rules ()
((_) (foo1 "B"))


((_ x) x)))
(W (foo))

which should give an error because foo1 should no be usable
outside the body of the let-syntax. Ikarus's expansion is a
bug in the implementation of let-syntax that you cited.

Aziz,,,

Abdulaziz Ghuloum

unread,
Oct 19, 2009, 6:22:22 AM10/19/09
to
On Oct 19, 7:30 am, Brian Mastenbrook <bmastenbr...@gmail.com> wrote:

> This is what this strategy leads to:
>
> #!r6rs
>
> (import (rnrs base (6))
>         (rnrs io simple (6)))
>
> (define display/newline (lambda (e) (display e) (newline)))
>
> (define-syntax my-begin (identifier-syntax begin))
>
> (let-syntax ((foo (syntax-rules () ((_) 'outer))))
>   (my-begin
>     (define-syntax foo (syntax-rules () ((_) 'inner)))
>     (display/newline (foo))))
>
> (let-syntax ((bar (syntax-rules () ((_) 'outer))))
>   (begin
>     (define-syntax bar (syntax-rules () ((_) 'inner)))
>     (display/newline (bar))))
>
> $ ikarus --r6rs-script test.ss
> inner
> outer

This has nothing to do with the issue but is a different bug.
The correct output should be: outer,outer.

> I think this should always be an error.

What makes it an error?

> My mental model is that the
> bindings established by a top-level `let-syntax' form are transformed
> into top-level definitions of identifiers that are renamed throughout
> the scope of the body. Here's an implementation of that idea:

> ...


> Do you have a convincing argument against this model?

I'm not arguing for or against anything -- -just explaining my
understanding/interpretation of R6RS's let-syntax.

Aziz,,,

Aaron W. Hsu

unread,
Oct 19, 2009, 11:57:08 AM10/19/09
to
On Mon, 19 Oct 2009 06:22:22 -0400, Abdulaziz Ghuloum <aghu...@gmail.com>
wrote:

>> I think this should always be an error.

> What makes it an error?

As I understand it, the body of a 'let-syntax' form is expanded in the
syntactic environment where the 'let-syntax' occurs, extended by the
bindings in the bindings form of the 'let-syntax'. R6RS' expansion
semantics seems to disallow the redefinition of keywords during expansion,
and since both the examples given above define a keyword in the body of
the 'let-syntax' that was bound previously by the 'let-syntax' bindings,
this appears to be illegal.

Abdulaziz Ghuloum

unread,
Oct 19, 2009, 12:12:53 PM10/19/09
to
On Oct 19, 6:57 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:
> On Mon, 19 Oct 2009 06:22:22 -0400, Abdulaziz Ghuloum <aghul...@gmail.com>  

> wrote:
>
> >> I think this should always be an error.
> > What makes it an error?
>
> As I understand it, the body of a 'let-syntax' form is expanded in the  
> syntactic environment where the 'let-syntax' occurs, extended by the
> bindings in the bindings form of the 'let-syntax'.

Yes.

> R6RS' expansion  
> semantics seems to disallow the redefinition of keywords during expansion,

Huh? This makes no sense to me, so, please restate. I see no
redefinitions
going on in any of these examples.

Aziz,,,

Aaron W. Hsu

unread,
Oct 19, 2009, 4:26:19 PM10/19/09
to Abdulaziz Ghuloum
On Mon, 19 Oct 2009 12:12:53 -0400, Abdulaziz Ghuloum <aghu...@gmail.com>
wrote:

> On Oct 19, 6:57 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:

Let me see if I can explain myself by walking through our case:

(let ()
(define (out x) (display x) (newline))
(let-syntax ([foo (lambda (_) "ls-foo")])
(define-syntax foo (lambda (_) "ds-foo"))
(out (foo)))
(out (foo)))

Let's start with an empty syntactic environment for expansion SE = {}.

To expand the body of the LET form, we first encounter the definition for
OUT, so we remember that OUT is a defined variable and defer the expansion
of the right hand side for later. Now we encounter the LET-SYNTAX. We
splice the body of the LET-SYNTAX form into the list of outer forms that
we are processing, replacing the LET-SYNTAX, and arrange for the syntactic
environment of these form to be SE = {(foo . t1)} where t1 is the location
of the lambda associated with foo. So, our list of forms to expand now
looks like:

(define-syntax foo (lambda (_) "ds-foo")) SE = {(foo . t1)}
(out (foo)) SE = {(foo . t1)}
(out (foo)) SE = {}

Here I have annotated the value of SE at each of these forms. The forms
surrounded by the LET-SYNTAX have the extended SE, whereas the outer
expression does not. The R6RS states that these inner expressions are
expanded within this new syntactic environment, but the scope of the
definitions is still the outer scope. So, let's continue expansion. We
encounter a DEFINE-SYNTAX form. Such a form's right hand side is evaluated
immediately, the result is bound to the keyword and this association is
placed in the current syntactic environment, thus extending that
environment for forms further down.

Se we go to insert (foo . t2) into the SE, but there is a problem. The
current syntactic environment of this definition already has a definition
for FOO in it. Whoops! This is not a legal definition, and so an error is
raised.

What you are suggesting, I believe, is that there are actually two
syntactic environments, one the environment in which the body of the
LET-SYNTAX is expanded, and one where new keyword definitions are placed,
namely, the outer environment. This means that LET-SYNTAX establishes a
new syntactic scope, but that definitions inside that scope affect an
environment which is not, in fact, the nearest syntactic environment
lexically. This seems to violate lexical scoping.

Another way of thinking about this is the following example:

(let-syntax ()
(define-syntax foo (lambda (_) "blah"))
(foo))
=> "blah"

Your model seems to suggest that the syntactic environment of the
LET-SYNTAX body is not affected by the syntax definition, and it instead
affects the outer syntactic environment. Okay, but then, for you to get
this result, the syntactic environment when (foo) is expanded must contain
'foo'. Either it is the outer environment, or the extended let-syntax
environment. If it is the former, then the extensions would not affect the
body, and let-syntax would be BEGIN, if the latter, then either the
extended environment is affected by the definition, or 'foo' cannot be
bound. If the environment is affected, then obviously we have a shadowing
of definitions in the above example. If not, then this latter example does
not work. Thus, one of these two examples should result in a raised
condition. You should not be able to have both without one of them raising
an error.

Aaron W. Hsu

unread,
Oct 19, 2009, 4:34:40 PM10/19/09
to Abdulaziz Ghuloum
On Mon, 19 Oct 2009 12:12:53 -0400, Abdulaziz Ghuloum <aghu...@gmail.com>
wrote:

> On Oct 19, 6:57 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:

Let me see if I can explain myself by walking through our case:

Aaron W. Hsu

Abdulaziz Ghuloum

unread,
Oct 19, 2009, 4:46:23 PM10/19/09
to
On Oct 19, 11:26 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:
...

Are you arguing that my understanding of R6RS is incorrect or that
R6RS's specification of let-syntax is incorrect?

Or, to put it another way, if I say:

(let ()
(define-syntax x (identifier-syntax 12))


(let-syntax ((x (identifier-syntax 13)))
x))

vs.

(let ()
(let-syntax ((x (identifier-syntax 13)))
(define-syntax x (identifier-syntax 12))
x))

What do you think the results of these should be according to R6RS?

And if it were up to you, what do you think the results should be?

Aziz,,,

Brian Mastenbrook

unread,
Oct 19, 2009, 4:51:18 PM10/19/09
to Brian Mastenbrook
On Oct 19, 5:22 am, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:

> This has nothing to do with the issue but is a different bug.
> The correct output should be: outer,outer.

If you fix this then the behavior of Ikarus on Derrick's first example
program will change, so it's a bit more than an unrelated issue.

Here's another issue for you:

#!r6rs

(import (rnrs base (6))
(rnrs io simple (6)))

(define display/newline (lambda (e) (display e) (newline)))

(let-syntax ((bar (syntax-rules () ((_) 'outer))))
(define-syntax bar (syntax-rules (bar)
((_ bar) 'correct)
((_ e) 'incorrect)))
(display/newline (bar bar)))

(display/newline (bar bar))

Both Ikarus and Larceny reject this program, saying that (bar bar) is
a syntax violation (in both uses, comment out the first to see the
error from the second). This doesn't make any sense.

>> I think this should always be an error.
>
> What makes it an error?

`let-syntax' establishes lexical bindings. As you said, a top-level
`let-syntax' does not establish a new lexical contour for the body.
Thus, the bindings established by the `let-syntax' form are in the
same lexical contour as the body. Thus:

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

is a duplicate definition of the identifier `foo', which is an error.

I believe any other semantic interpretation is going to behave oddly
with respect to free identifier matching in one way or another.

Abdulaziz Ghuloum

unread,
Oct 19, 2009, 4:55:46 PM10/19/09
to
On Oct 19, 11:46 pm, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:
> On Oct 19, 11:26 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:
> ...
>
> Are you arguing that my understanding of R6RS is incorrect or that
> R6RS's specification of let-syntax is incorrect?
>
> Or, to put it another way, if I say:
>
> (let ()
>   (define-syntax x (identifier-syntax 12))
>   (let-syntax ((x (identifier-syntax 13)))
>     x))
>
> vs.
>
> (let ()
>   (let-syntax ((x (identifier-syntax 13)))
>     (define-syntax x (identifier-syntax 12))
>     x))

Add two more examples to cover all bases:

(let ()
(let-syntax ((x (identifier-syntax 13)))

(define-syntax x (identifier-syntax 12)))
x)

(let ()
(define-syntax x (identifier-syntax 12))
(let-syntax ((x (identifier-syntax 13)))

#|yes empty|#)
x)

Abdulaziz Ghuloum

unread,
Oct 19, 2009, 5:03:15 PM10/19/09
to
On Oct 19, 11:51 pm, Brian Mastenbrook <bmastenbr...@gmail.com> wrote:
> On Oct 19, 5:22 am, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:
>
> > This has nothing to do with the issue but is a different bug.
> > The correct output should be: outer,outer.
>
> If you fix this then the behavior of Ikarus on Derrick's first example
> program will change, so it's a bit more than an unrelated issue.

It's bizarre to make such comment because, you know, there are more
than
two ways to do something. Anyhow, I did fix that bug that your my-
begin
exposed and Derick's first example didn't change of course.


> Here's another issue for you:

> ...


> (let-syntax ((bar (syntax-rules () ((_) 'outer))))
>   (define-syntax bar (syntax-rules (bar)
>                        ((_ bar) 'correct)
>                        ((_ e) 'incorrect)))
>   (display/newline (bar bar)))

> ...


> Both Ikarus and Larceny reject this program, saying that (bar bar) is
> a syntax violation (in both uses, comment out the first to see the
> error from the second). This doesn't make any sense.

According to you or to R6RS?
[please answer my 2 questions about the 4 examples I posted]

Aziz,,,

Brian Mastenbrook

unread,
Oct 19, 2009, 8:49:47 PM10/19/09
to Brian Mastenbrook
On Oct 19, 3:46 pm, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:
> On Oct 19, 11:26 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:
> ...
>
> Are you arguing that my understanding of R6RS is incorrect or that
> R6RS's specification of let-syntax is incorrect?
>
> Or, to put it another way, if I say:
>
> (let ()
>   (define-syntax x (identifier-syntax 12))
>   (let-syntax ((x (identifier-syntax 13)))
>     x))
>
> vs.
>
> (let ()
>   (let-syntax ((x (identifier-syntax 13)))
>     (define-syntax x (identifier-syntax 12))
>     x))
>
> What do you think the results of these should be according to R6RS?
>
> And if it were up to you, what do you think the results should be?

First example: 13. I don't think anyone would argue about this. The
binding established by `let-syntax' has the <form>s as their region
(sayeth 11.18). Thus, the use of `x' refers to the binding established
by `let-syntax'.

Second example: Arguably 13, but I believe there is an inherent
contradiction in the R6RS surrounding this point which your example
does not expose, and that the resolution to this contradiction makes
this case an error. Consider:

(let-syntax ((foo (syntax-rules () ((_ e ...) 'outer))))
(define-syntax foo (syntax-rules ()
((_) (foo ()))
((_ e) 'inner)))
(display/newline (foo)))

(display/newline (foo))

Quoth 5.2: "Every mention of an identifier refers to the binding of
the identifier that establishes the innermost of the regions
containing the use." Thus, the use of `foo' on the right-hand side of
the `define-syntax' form must refer to the binding established by the
`let-syntax' form, as it has the innermost region. But, quoth 11.2.2:


"All bindings established by a set of definitions, whether keyword or
variable definitions, are visible within the definitions themselves."

One could consider the region of the `define-syntax' to be
discontinuous, but I believe that this violates the intent of the
standard where it describes Scheme as having block structure, as well
as being something that is likely to cause issues with free identifier
matching.

For what it's worth, Ikarus is in violation of 11.2.2 on this point,
as the second (foo) returns 'outer, not 'inner.

I prefer to resolve the implicit contradiction by making this case an
error. I don't see another way of doing so that makes sense. In
particular, I believe that:

(define-syntax foo (syntax-rules ()
((_ v) (begin
(define v 1)
v))))

Should evaluate to 1 in all contexts where it is valid. Other
interpretations would be incredibly confusing to macro authors.

On Oct 19, 3:55 pm, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:
> Add two more examples to cover all bases:
>
> (let ()
>   (let-syntax ((x (identifier-syntax 13)))
>     (define-syntax x (identifier-syntax 12)))
>   x)
>
> (let ()
>   (define-syntax x (identifier-syntax 12))
>   (let-syntax ((x (identifier-syntax 13)))
>     #|yes empty|#)
>   x)
>
> > What do you think the results of these should be according to R6RS?
>
> > And if it were up to you, what do you think the results should be?

First case: The only way I can figure to make sense of the internal
contradiction described above is to make this case an error.

Second case: 12.

Abdulaziz Ghuloum

unread,
Oct 21, 2009, 5:21:59 AM10/21/09
to
On Oct 20, 3:49 am, Brian Mastenbrook <bmastenbr...@gmail.com> wrote:

> > (let ()
> >   (define-syntax x (identifier-syntax 12))
> >   (let-syntax ((x (identifier-syntax 13)))
> >     x))

> > (let ()


> >   (let-syntax ((x (identifier-syntax 13)))
> >     (define-syntax x (identifier-syntax 12))
> >     x))

> First example: 13. I don't think anyone would argue about this. The


> binding established by `let-syntax' has the <form>s as their region
> (sayeth 11.18). Thus, the use of `x' refers to the binding established
> by `let-syntax'.

We all agree on this one.

> Second example: Arguably 13,

I believe the answer *should* be 13. I also believe that R6RS
requires
it to be 12 for the reasons I outlined previously, namely, that let-
syntax
does not have a body of its own for the definitions that occur inside
of
it.
[note: even the syntax of let-syntax says
(let-syntax <bindings> <forms> ...)
and not
(let-syntax <bindings> <body> ...)
or
(let-syntax <bindings> <definition> ...).]


> but I believe there is an inherent
> contradiction in the R6RS surrounding this point which your example
> does not expose, and that the resolution to this contradiction makes
> this case an error. Consider:
>
> (let-syntax ((foo (syntax-rules () ((_ e ...) 'outer))))
>   (define-syntax foo (syntax-rules ()
>                        ((_) (foo ()))
>                        ((_ e) 'inner)))
>   (display/newline (foo)))
>
> (display/newline (foo))
>
> Quoth 5.2: "Every mention of an identifier refers to the binding of
> the identifier that establishes the innermost of the regions
> containing the use." Thus, the use of `foo' on the right-hand side of
> the `define-syntax' form must refer to the binding established by the
> `let-syntax' form, as it has the innermost region.

Correct.

> But, quoth 11.2.2:
> "All bindings established by a set of definitions, whether keyword or
> variable definitions, are visible within the definitions themselves."
> One could consider the region of the `define-syntax' to be
> discontinuous, but I believe that this violates the intent of the
> standard where it describes Scheme as having block structure, as well
> as being something that is likely to cause issues with free identifier
> matching.

I don't understand that.

Consider this:

(let ((x0 ---))
(define x1 ---)
(let-syntax ((x2 ---))
(define x3 ---)
(let ((x4 ---))
---)))

According to R6RS, there is a block B1 containing x0 established by
the
bindings of let; a block B2 containing x1 established by the internal
definitions of the let; a block B3 containing x2 established by the
bindings of let-syntax; and a block B4 containing x4 established by
the
inner let. The let-syntax does not have a body and it splices its
forms
into the outer definitions, making x3 part of B2.

If you want a let-syntax that has its own body, you can certainly do
that by wrapping its forms with a (let () ---). On the other hand, if
let-syntax has a body (or an implicit let), that makes it impossible
to
get the other behavior.

> For what it's worth, Ikarus is in violation of 11.2.2 on this point,
> as the second (foo) returns 'outer, not 'inner.

No. [the fact that you guys keep on interchanging inner with outer
does not change the fact that outer is inner and inner is outer
according to R6RS]

> I prefer to resolve the implicit contradiction by making this case an
> error. I don't see another way of doing so that makes sense.

Your preferences and lack of imagination noted. :-)

> In particular, I believe that:
>
> (define-syntax foo (syntax-rules ()
>                      ((_ v) (begin
>                               (define v 1)
>                               v))))
>
> Should evaluate to 1 in all contexts where it is valid. Other
> interpretations would be incredibly confusing to macro authors.

I agree with this. What I'm saying is that R6RS requires the
behavior that you find confusing, not that R6RS's behavior is
the right thing.


> On Oct 19, 3:55 pm, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:
> > Add two more examples to cover all bases:
>
> > (let ()
> >   (let-syntax ((x (identifier-syntax 13)))
> >     (define-syntax x (identifier-syntax 12)))
> >   x)
>
> > (let ()
> >   (define-syntax x (identifier-syntax 12))
> >   (let-syntax ((x (identifier-syntax 13)))
> >     #|yes empty|#)
> >   x)
>
> > > What do you think the results of these should be according to R6RS?
>
> > > And if it were up to you, what do you think the results should be?
>
> First case: The only way I can figure to make sense of the internal
> contradiction described above is to make this case an error.

I believe the first case should be 12, and R6RS requires it to be 12.

> Second case: 12.

We all agree on this one.

Aziz,,,

Brian Mastenbrook

unread,
Oct 21, 2009, 3:56:11 PM10/21/09
to Brian Mastenbrook
On Oct 21, 4:21 am, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:
> > But, quoth 11.2.2:
> > "All bindings established by a set of definitions, whether keyword or
> > variable definitions, are visible within the definitions themselves."
> > One could consider the region of the `define-syntax' to be
> > discontinuous, but I believe that this violates the intent of the
> > standard where it describes Scheme as having block structure, as well
> > as being something that is likely to cause issues with free identifier
> > matching.
[snip]

> > For what it's worth, Ikarus is in violation of 11.2.2 on this point,
> > as the second (foo) returns 'outer, not 'inner.
>
> No.  [the fact that you guys keep on interchanging inner with outer
> does not change the fact that outer is inner and inner is outer
> according to R6RS]

The meaning of 11.2.2 seems clear to me: in a definition of the form
(define x (lambda () ... x ...)), the `x' that is referred to in the
init must be the `x' that is being defined. If it doesn't mean that,
what does it mean?

Aaron W. Hsu

unread,
Oct 25, 2009, 8:00:46 PM10/25/09
to
On Mon, 19 Oct 2009 16:46:23 -0400, Abdulaziz Ghuloum <aghu...@gmail.com>
wrote:

> (let ()


> (define-syntax x (identifier-syntax 12))
> (let-syntax ((x (identifier-syntax 13)))
> x))

Naturally, this is 13.

> (let ()
> (let-syntax ((x (identifier-syntax 13)))
> (define-syntax x (identifier-syntax 12))
> x))
>
> What do you think the results of these should be according to R6RS?

I'm inclined to think that an error should occur in the second, but I'm
willing to admit that you could probably try to interpret it as something
different. If you did interpret it differently, I think you *could* get
either 13 or 12 out of it reasonably.

> And if it were up to you, what do you think the results should be?

In the second, if we didn't allow this to be an error, this should be 12.

Aaron W. Hsu

unread,
Oct 25, 2009, 8:03:19 PM10/25/09
to
On Mon, 19 Oct 2009 16:55:46 -0400, Abdulaziz Ghuloum <aghu...@gmail.com>
wrote:

> (let ()


> (let-syntax ((x (identifier-syntax 13)))
> (define-syntax x (identifier-syntax 12)))
> x)

R6RS: 12 or error. Personally, I like the error.

> (let ()
> (define-syntax x (identifier-syntax 12))
> (let-syntax ((x (identifier-syntax 13)))
> #|yes empty|#)
> x)

12.

andre

unread,
Oct 31, 2009, 4:54:28 PM10/31/09
to
On Oct 21, 2:21 am, Abdulaziz Ghuloum <aghul...@gmail.com> wrote:
> On Oct 20, 3:49 am, Brian Mastenbrook <bmastenbr...@gmail.com> wrote:
>
> > > (let ()
> > >   (let-syntax ((x (identifier-syntax 13)))
> > >     (define-syntax x (identifier-syntax 12))
> > >     x))
>
> > Arguably 13,
>
> I believe the answer *should* be 13.  I also believe that R6RS
> requires
> it to be 12 for the reasons I outlined previously, namely, that let-
> syntax
> does not have a body of its own for the definitions that occur inside
> of
> it.

Hmm. My reading of R6RS suggests that it should in fact be 13, for
precisely
the same reason you used to argue that it should be 12! :-)

FWIW, despite the fact that my implementation (= the one used in
Larceny)
indeed gives 13, it took me quite some time to trace and clarify why.

One can give a nice algebraic interpretation of this, which I think
is a valid interpretation of r6rs, namely, push down any let-syntax
recursively to all enclosed references and splice, so

(let ()
(let-syntax ((x (identifier-syntax 13)))
(define-syntax x (identifier-syntax 12))
x))

-->

(let ()
(define-syntax x (identifier-syntax 12))
(let-syntax ((x (identifier-syntax 13)))
x))

I think then the following should give 13, but I am not
sure (I believe my implementation has a bug)

(let ()
(let-syntax ((x (identifier-syntax 13)))

(define x x))
x)

Anyone agree or disagree?

Andre

andre

unread,
Oct 31, 2009, 5:25:16 PM10/31/09
to
On Oct 31, 1:54 pm, andre <andreuri2...@yahoo.com> wrote:

> One can give a nice algebraic interpretation of this, which I think
> is a valid interpretation of r6rs, namely, push down any let-syntax
> recursively to all enclosed references and splice, so

Correction - make that: "all enclosed FORMS".

> I think then the following should give 13, but I am not
> sure (I believe my implementation has a bug)
>
>    (let ()
>      (let-syntax ((x (identifier-syntax 13)))
>        (define x x))
>      x)

Then this should not be 13. Instead, an undefined reference error
would be
the correct behaviour (as checked in Larceny).

Andre

Aaron W. Hsu

unread,
Oct 31, 2009, 5:31:52 PM10/31/09
to
On Sat, 31 Oct 2009 16:54:28 -0400, andre <andreu...@yahoo.com> wrote:

> One can give a nice algebraic interpretation of this, which I think
> is a valid interpretation of r6rs, namely, push down any let-syntax
> recursively to all enclosed references and splice, so
>
> (let ()
> (let-syntax ((x (identifier-syntax 13)))
> (define-syntax x (identifier-syntax 12))
> x))
>
> -->
>
> (let ()
> (define-syntax x (identifier-syntax 12))
> (let-syntax ((x (identifier-syntax 13)))
> x))
>
> I think then the following should give 13, but I am not
> sure (I believe my implementation has a bug)
>
> (let ()
> (let-syntax ((x (identifier-syntax 13)))
> (define x x))
> x)
>
> Anyone agree or disagree?

My reading of the R6RS and what I think should happen doesn't jive with
the above. I don't think that algebraic transformation is accurate. My
transformation would be more like the following:

(let ()
(let-syntax ([x (identifier-syntax 13)])
(define-syntax x (identifier-syntax 12))
(display x))
(display x))

==>

(let ()
(define-syntax x1 (identifier-syntax 13))
(define-syntax x2 (identifier-syntax 12))
(display x2)
(display x2))

That is, I think that the closer X definition should shadow things in its
scope, and in the above, I believe that both X variable references are in
the scope of the second X. The let-syntax binding is shadowed by the
'define-syntax' binding. The use of 'let-syntax' is really about limiting
the visibility of an identifier to a set of forms without introducing a
new scope, like below.

(let ()
(define-syntax x (identifier-syntax 12))

(let-syntax ([x (identifier-syntax 13)])
(display x))
(display x))

==>

(let ()
(define-syntax x1 (identifier-syntax 12))
(define-syntax x2 (identifier-syntax 13))
(display x2)
(display x1))

But even this isn't quite right, because that transformation assumes that
'let-syntax' is nothing more than a definition, which isn't true. It
extends a syntactic environment, which enables it to shadow bindings
legally. The 'define-syntax' form, by definition, when expanded, is not
supposed to shadow any bindings, and that's what makes the 'define-syntax'
of x inside of the 'let-syntax' that already binds x an error. So,
'let-syntax allows you to shadow bindings, and limit their visibility, but
does not introduce new scope: it just expands the forms inside of it
within an extended syntactic environment, but the same scope as the place
where the let-syntax occurs.

Chez Scheme and PLT seem to agree with me here, either on purpose or by
accident. Even if this isn't the case, I can't see in practice anything
goode coming from not making this an error that doesn't also introduce a
lot of bad, so I"m going to say that the 'define-syntax' inside of
'let-syntax' example should be an error.

andre

unread,
Oct 31, 2009, 6:23:03 PM10/31/09
to
On Oct 31, 2:31 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:

> My reading of the R6RS and what I think should happen doesn't jive with  
> the above. I don't think that algebraic transformation is accurate. My  
> transformation would be more like the following:
>
> (let ()
>    (let-syntax ([x (identifier-syntax 13)])
>      (define-syntax x (identifier-syntax 12))
>      (display x))
>    (display x))
>
> ==>
>
> (let ()
>    (define-syntax x1 (identifier-syntax 13))
>    (define-syntax x2 (identifier-syntax 12))
>    (display x2)
>    (display x2))

No, your suggested transformation would not be correct. To see
that it is incorrect, consider that it breaks down in the
following example:

(+ (let-syntax ((x (identifier-syntax 12)))
x))

Andre

Aaron W. Hsu

unread,
Oct 31, 2009, 6:28:01 PM10/31/09
to

I mentioned this limitation further down in my email, and was only using
this transformation to illustrate why I thought that this should raise an
error. The bindings simply extend the syntactic environment for a limited
amount of forms being expanded, not really doing definitions, which is why
they can shadow bindings that come before them.

andre

unread,
Oct 31, 2009, 6:37:49 PM10/31/09
to
On Oct 31, 2:31 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:

> 'let-syntax allows you to shadow bindings, and limit their visibility, but  
> does not introduce new scope: it just expands the forms inside of it  
> within an extended syntactic environment, but the same scope as the place  
> where the let-syntax occurs.

I agree, but isn't what you are saying here in words exactly what my
transformation does?

I.e.,

(let ()
(let-syntax ((x (identifier-syntax 13)))
(define-syntax x (identifier-syntax 12))
x))

-->

(let ()
(define-syntax x (identifier-syntax 12))
(let-syntax ((x (identifier-syntax 13)))
x))

Here, as you say, "the forms inside of it are


within an extended syntactic environment, but the same scope
as the place where the let-syntax occurs."

Andre

andre

unread,
Oct 31, 2009, 6:40:54 PM10/31/09
to

Sorry, I left out a step:

(let ()
(let-syntax ((x (identifier-syntax 13)))
(define-syntax x (identifier-syntax 12))
x))

-->

(let ()
(let-syntax ((x (identifier-syntax 13)))

(define-syntax x (identifier-syntax 12)))


(let-syntax ((x (identifier-syntax 13)))
x))

-->

(let ()
(define-syntax x (identifier-syntax 12))

13)

Aaron W. Hsu

unread,
Oct 31, 2009, 7:10:04 PM10/31/09
to

No, because you're altering the order of operations. You're claiming that
the 'define-syntax should extend an environment before that same
environment is extended by the 'let-syntax' form, but in my opinion, this
is incorrect.

In other words, you're messing with the lexical scoping (yes, I know that
there isn't a new scope introduced) here. The closest reference to X when
the X is referenced in this form is the 'define-syntax' form, not the
binding in the 'let-syntax' form. In the same way that you wouldn't expect
(let ([x 5]) (define x 3) x) => 5, you shouldn't expect 'let-syntax' to do
that either. The document says that the 'define-syntax' is expanded in the
syntactic environment already containing an 'x' definition at the same
(albeit specially created) "scope." You can't redefine a syntax in that
same scope, so it should signal an error. Otherwise, if this should not
signal an error (I think it should), then it should be the 'define-syntax'
'x' that is referenced, not the other one.

So, here's how I see it in step-by-step form:

(let-syntax ([x (identifier-syntax 13)])
(define-syntax x (identifier-syntax 12))

x)

1. Encounter 'let-syntax'.

2. Encounter bindings, so take current syntactic environment and extend
the bindings with those encountered.

3. Start expanding forms.

4. Encounter definition of 'x' as a keyword. Attempt to extend current
syntactic environment with an x keyword bindings (no new scope).

[Alternative one, that I think should take place.]

5a. Encounter the already bound 'x' at the same "scope" as the keyword 'x'
we are trying to bind, so signal an error/

[Alternative that is the one I think should happen if 5a is not correct.]

5b. Replace the 'x' binding bound by 'let-syntax' with the new binding for
the keyword 'x'.

6. Encounter x, expand it to 12.

Aaron W. Hsu

unread,
Oct 31, 2009, 7:34:54 PM10/31/09
to
On Sat, 31 Oct 2009 18:40:54 -0400, andre <andreu...@yahoo.com> wrote:

> On Oct 31, 3:37 pm, andre <andreuri2...@yahoo.com> wrote:
>> On Oct 31, 2:31 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:
>>
>> > 'let-syntax allows you to shadow bindings, and limit their
>> visibility, but > does not introduce new scope: it just expands the
>> forms inside of it > within an extended syntactic environment, but the
>> same scope as the place > where the let-syntax occurs.
>>
>> I agree, but isn't what you are saying here in words exactly what my
>> transformation does?

> (let ()
> (let-syntax ((x (identifier-syntax 13)))
> (define-syntax x (identifier-syntax 12))
> x))
>
> -->
>
> (let ()
> (let-syntax ((x (identifier-syntax 13)))
> (define-syntax x (identifier-syntax 12)))
> (let-syntax ((x (identifier-syntax 13)))
> x))
>
> -->
>
> (let ()
> (define-syntax x (identifier-syntax 12))
> 13)
>

Let me see if I can develop a transformation based on the marks and
substitutions model of section 12.1.

(let-syntax ([x (identifier-syntax 13)])
(define-syntax x (identifier-syntax 12))

x)
x

Since marks on this are all the same, I'll just show the substitutions as
#[id substs] where 'substs' is a list. We'll start with an empty
substitution being applied to the variable reference. For clarity, I'm not
going to wrap the whole thing, and just focus on the wrappings of the
variable references.

(let-syntax ([x (identifier-syntax 13)])
(define-syntax x (identifier-syntax 12))

#[x . ()])
#[x . ()]

Now, since we have encountered a binding form, let's make a location l1
for the '13' transformer and create a substitution (x . l1) for the
transformer bound to x by the 'let-syntax' form. Now, we need to apply
this substitution to all forms which should have this binding visible to
them.

(define-syntax x (identifier-syntax 12))
#[x . ((x . l1))]
#[x . ()]

Notice that we're not done with the 'let-syntax' form, and we splice in
the forms from the 'let-syntax' form into the context where the
'let-syntax' form occurred. The substitution created by the 'let-syntax'
form only applies to the forms appearing within it, so only the first 'x'
gets a substitution applied to it. No new scope has been introduced, but
the correct visibility of the 'x' variable has been maintained so far.

Now, we need to expand the 'define-syntax' form. Here, this is another
binding form, so we create another substitution and apply the substitution
to those identifiers for whom the binding should be visible. Since the
scope of the 'define-syntax' form is the whole thing, we get something
like the following, assuming that we have l2 being the location of the
'12' transformer.

#[x . ((x . l2) (x . l1))]
#[x . ((x . l2))]

So now, we encounter the 'x' reference with the two bindings visible. The
rules of resolution in Section 12.1 state that the latest matching
substitution that was applied should be used. We look for that and
discover that l2 is the latest matching substitution, so we use that, and
the expansion is "12." Likewise, the second reference goes along in the
same way.

Now, this all leaves out the fact that the binding for the 'x' keyword by
the 'define-syntax' should have caused an error to happen because it was
trying to bind a keyword that had already been bound.

andre

unread,
Nov 1, 2009, 9:11:50 AM11/1/09
to
On Oct 31, 3:10 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:

> No, because you're altering the order of operations. You're claiming that  
> the 'define-syntax should extend an environment before that same  
> environment is extended by the 'let-syntax' form, but in my opinion, this  
> is incorrect.
>
> In other words, you're messing with the lexical scoping (yes, I know that  
> there isn't a new scope introduced) here. The closest reference to X when  
> the X is referenced in this form is the 'define-syntax' form, not the  
> binding in the 'let-syntax' form. In the same way that you wouldn't expect  
> (let ([x 5]) (define x 3) x) => 5, you shouldn't expect 'let-syntax' to do  
> that either.

I don't think the comparison is applicable. LET does introduce a new
scope whereas LET-SYNTAX doesn't.

> So, here's how I see it in step-by-step form:
>
> (let-syntax ([x (identifier-syntax 13)])
>    (define-syntax x (identifier-syntax 12))
>    x)
>
> 1. Encounter 'let-syntax'.
>
> 2. Encounter bindings, so take current syntactic environment and extend  
> the bindings with those encountered.
>
> 3. Start expanding forms.
>
> 4. Encounter definition of 'x' as a keyword. Attempt to extend current  
> syntactic environment with an x keyword bindings (no new scope).

No, this cannot be correct in my opinion. This binding should
extend the environment of the /enclosing/ region, e.e., the body in
progress
/before/ the let-syntax was encountered. That is what I think it
means
for let-syntax to be a /splicing/ form - any definitions inside
it should be added to the /enclosing/ environment of the body already
in
progress /before/ the let-syntax was encountered,
not to the nested environment introduced by the let-syntax.

To further motivate this, consider what happens when you leave
the let-syntax and continue with the rest of the enclosing
body. It is clear that now you should discard the nested
environment introduced by the let-syntax but you should keep
the binding introduced by the definition

(define-syntax x (identifier-syntax 12))

which to me suggests that you should add this binding to the
enclosing environment, which is kept, and not, as you suggest, to
the nested environment of the let-syntax, since the latter is
discarded.

Andre

andre

unread,
Nov 1, 2009, 9:35:14 AM11/1/09
to
On Oct 31, 3:34 pm, "Aaron W. Hsu" <arcf...@sacrideo.us> wrote:

> Now, we need to expand the 'define-syntax' form. Here, this is another  
> binding form, so we create another substitution and apply the substitution  
> to those identifiers for whom the binding should be visible. Since the  
> scope of the 'define-syntax' form is the whole thing, we get something  
> like the following, assuming that we have l2 being the location of the  
> '12' transformer.
>
> #[x . ((x . l2) (x . l1))]
> #[x . ((x . l2))]

This suggestion presupposes that the binding (x . 12) whose region
is the /enclosing/ scope should shadow the binding valid in the
/nested/ let-syntax region.

I don't believe it is consistent with the rest of Scheme for
an outer binding to shadow a nested binding.

Andre

0 new messages