I am reverting to the renaming semantics Larceny has been using for
10 years. That means tonight's nightly builds will not be able to
run the sample implementation of SRFI 148. I will explain why in a
moment.
Marc Nieper-Wißkirchen wrote:
> Yes, I think in fact the main point that needs to be resolved is whether a
> pattern variable is bound (for the purposes of hygiene) by a syntax rule as
> much as a variable is bound by a lambda form.
In Larceny, pattern variables are indeed bound, but I don't think
that's important. That binding happens after (and has to happen
after) the matching that determines whether the identifiers are
pattern variables or literals.
> From the view of a macro writer, the syntax-rules system is more powerful
> when pattern variables are being renamed, because this would allow me to
> generate arbitrary many pattern variables by the usual tricks.
That renaming is the reason your sample implementation doesn't work
in Larceny (with the reverted semantics). If an identifier occurs
within a template but not within the corresponding pattern, it is
renamed when the template is transcribed. Those identifiers do
indeed act as generated pattern variables, which is the problem.
For your sample implementation to work, some of them need to be
treated as literals instead of pattern variables.
In the sample implementation of SRFI 147 (not 148), an identifier
named :continuation is inserted by
the 4th rule of expand-transformer
the 1st rule of syntax-rules-aux
the 2nd rule of syntax-rules-aux
the 4th rule of syntax-rules-aux
Those inserted identifiers are renamed for hygiene. That means the
identifiers named :continuation that are introduced by the 1st and
2nd rules of syntax-rules-aux, in the hope that they will end up as
literals, will not match the identifier named :continuation that is
introduced by the fourth rule of expand-transformer or by the fourth
rule of syntax-rules-aux. Instead of being recognized as literals,
which appears to be your intention, the :continuation identifiers
introduced by the fourth rules of those two macros will act as pattern
variables.
That is why your sample implementation doesn't run in Larceny. Here
is an example:
(let-syntax
((m (em-syntax-rules ()
((m 'a) 'a))))
(m '(define x 10))
x)
eventually expands into an expression whose de-mangled form is
(begin
(define-syntax
o
(em-syntax-rules-aux2
o
free-identifier=?
()
...
()
(((m 'a) 'a))
()))
(expand-transformer
(let-syntax-aux (m) () () ((m '(define x 10)) x))
o))
The define-syntax form expands into something like
(expand-transformer (scheme-define-syntax o)
(em-syntax-rules-aux2
o
free-identifier=?
()
...
()
(((m 'a) 'a))
()))
which matches the 4th rule of expand-transformer so it becomes
something like
(em-syntax-rules-aux2
(:continuation&&91
expand-transformer
(scheme-define-syntax o))
o
free-identifier=?
()
...
()
(((m 'a) 'a))
())
I have written :continuation&&91 to remind you that :continuation
has been renamed. This is a tentative renaming, which will end up
resolving to the same binding as :continuation if it ends up as a
free variable of the final output, but that isn't what's going to
happen. The form above eventually turns into something like
(scheme-define-syntax
o
(scheme-syntax-rules
(quote :prepare :call :continuation&&87)
((_ (:continuation&&91 c ...) :prepare s %x)
(c ... (ck s "arg" (o) %x)))
((_ (:continuation&&90 c ...) :prepare s . args)
(c ...
(syntax-error
"bad arguments to macro call"
.
args)))
((_ (:continuation&&89 c ...) :call s 'a)
(c ... (ck s 'a)))
((_ (:continuation&&88 c ...) . args)
(c ... (ck () (o . args))))
((_ :prepare s %x) (ck s "arg" (o) %x))
((_ :prepare s . args)
(syntax-error
"bad arguments to macro call"
.
args))
((_ :call s 'a) (ck s 'a))
((_ . args) (ck () (o . args)))))
Skipping a few steps, the macro expander has to figure out
whether the four renamed occurrences of :continuation are
pattern variables or literals. To decide which, the macro
expander has to match :continuation&&91 (say) against the
literal :continuation&&87. Those don't match unless you ignore
the renaming, in which case other bad things happen as you
pointed out, so they don't match. That means all of the
:continuation identifiers in the four rules where they appear
are treated as pattern variables, which causes the fourth rule
to match when the last was intended. That's why
(m '(define x 10))
turns into
((define x 10) (ck () (o)))
instead of
(ck () (o '(define x 10)))
which is why Larceny reports a definition outside any legal
context.
Now the mystery is why the sample implementation works with
all the other systems you tried. They might be renaming less
aggressively than Larceny, or they might be using a different
semantics when matching identifiers that occur in patterns
against the literals.
If you had to add a definition of :continuation to get your
implementation to work in those systems, that's a clue. They
might be resolving the bindings of identifiers just before
they decide whether the identifiers are pattern variables or
literals, and that resolution might fail for some reason if
:continuation isn't bound. That sounds very much like using
free-identifier=?, however, which (as you pointed out) does
the wrong thing for a different example.
Will