At Tue, 29 Sep 2015 18:20:38 -0500, Brian Mastenbrook wrote:
> In the scope sets document
> (
https://www.cs.utah.edu/~mflatt/scope-sets-5/pattern-macros.html#%28part._intd
> ef%29) it is claimed that the following is ambiguous:
>
> (define-syntax-rule (def-m m given-x)
> (begin
> (define x 1)
> (define-syntax-rule (m)
> (begin
> (define given-x 2)
> x))))
>
> (def-m m x)
> (m)
>
> Rather confusingly, it is only ambiguous when `def-m' is used in the same
> definition context as where `def-m' is defined; the following is accepted with
> the same interpretation as the old expander:
>
> (let ()
> (def-m m x)
> (m))
The relevant feature of the original example is that `def-m` and `m`
are defined in the same definition context. If you put the definition
of `m` in a more nested context, then it defeats the attempt at
ambiguity.
> After playing with various permutations of this, I encountered a
> behavior that seems like a clear violation of referential
> transparency:
>
> (define x 'outer)
>
> (define-syntax-rule (def-m m given-x)
> (define-syntax-rule (m)
> (begin
> (define given-x 'inner)
> x)))
>
> (def-m m x)
> (let ()
> (m))
>
> Under the old expander, this yields 'outer; under the new expander, it yields
> 'inner.
>
> The definition introduced by (define given-x 'inner) should never capture the
> use of `x' in `m', at least according to my mental model: either a given use
> of `m' should provoke a duplicate binding error, or it should refer to the
> outer `x'. I'm unable to find any convincing rationalization for why it would
> behave otherwise. What am I missing?
Let's simplify a little, since the line between the definition and use
of `m` is irrelevant:
(define x 'outer)
(define-syntax-rule (def-and-use-m given-x)
(begin
(define-syntax-rule (m)
(begin
(define given-x 'inner)
x))
(m)))
(def-and-use-m x)
The old expander gives 'outer, the new one gives 'inner. The results
are the same if you wrap a `(let () ....)` around `(m)`.
When you step back, it sure looks non-hygienic, because
(define-syntax-rule (def-and-use-m given-x)
(begin
(define-syntax-rule (m)
(begin
(define given-x 'inner)
x))
(m)))
looks like a reference to a `x` that should never be captured by any
`given-x`. The catch is that it's a free `x` with respect to the
context of `def-and-use-m`. In a context that allows both a definition
and a use of `def-and-use-m`, it can turn out that `given-x` is exactly
the identifier that the free `x` should refer to.
If you put `(def-and-use-m x)` under a `(let () ....)`, then can never
be the same `x`. But after `m` is defined in the same context as
`def-and-use-m`, it doesn't matter whether a use `(m)` is under a `(let
() ....)`.
For this kind of example, the difference between the old and new
expanders boils down to whether the following is true:
if `x` refers to a particular binding, then absent any other bindings
with the name "x", a macro introduction of `x` also refers to the
same binding
That's true for the next expander, because a macro introduction is just
an extra scope. It's false for the old expander, because various mark
and rename orderings matter (and benefits of the new expander often
have to do with avoiding dependencies on order).