[racket] Another macro question: making a lexically binding keyword?
Yesterday, Danny Yoo wrote:
> Let's say that I have the following toy iteration form for doing
> something over a sequence of numbers:
> [...]
>
> However, as Brian Mastenbrook notes, the use of syntax parameters
> here can be troublesome because they don't necessarily work
> lexically.
It's worth repeating: this claim is confusing scopes of pattern
variable bindings at the syntax level with the scope of generated
code. Ryan had a very good demonstration of that, which I don't
remember now. (Ryan?)
> For example, we might be devious and do this:
>
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> (define-syntax (interfere stx)
> (syntax-case stx ()
> [(_ body ...)
> #'(ranged 1 body ...)]))
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>
> The interfere macro here should be a no-op.
Then this implementation is a *bug*, since `INDEX' is externally
visible. And I'll also repeat the obvious analogy by translating your
code:
(define INDEX (make-parameter #f))
(define (ranged n thunk)
(for ([k n]) (parameterize ([INDEX k]) (thunk))))
This works as expected:
(ranged 5 (λ() (displayln (INDEX))))
This should be a no-op:
(define (interfere thunk)
(ranged 1 thunk))
but it's not:
(ranged 5 (λ() (interfere (λ() (displayln (INDEX))))))
I would have liked to see the above instead -- which means that my
`interfere' implementation is buggy. I need to do something extra to
resolve it, eg:
(define (interfere thunk)
(define saved (INDEX))
(ranged 1 (λ() (parameterize ([INDEX saved]) (thunk)))))
and now it works.
Going back to syntax parameters:
> I need to do something extra, since the use of interfere adds an
> additional syntax parameterization that breaks the lexicalness I
> expected from INDEX.
there is no breakage here: the way that your `interfere' is broken is
essentially the feature that syntax parameters provide: making it
possible to have new forms that will adjust the meaning of `INDEX'.
If you don't want that, then you can use the syntax equivalent of the
above fixed `interfere'.
> I can do something that appears to do the trick, but I'm a bit
> uncomfortable with it: [...]
I didn't try to actually follow the fix, but I believe that the basic
intuition should be that the above is the main *feature* of syntax
parameters, and if you try to "fix" it, then you shouldn't use them in
the first place, otherwise you're re-inventing unhygienic syntax.
Following this intution, I wrote an obvious example that abstracts
over `ranged' in a way that makes `INDEX' still work:
(define-syntax-rule (5-times E) (ranged 5 E))
(5-times (displayln INDEX))
and unsurprisingly, this abstraction works nicely in the first
example, but with your fix I get:
expand: unbound identifier in module in: internal-index-id18
> The use of the gensym there is what makes me uncomfortable.
(That looks like the core of the unhygienic reinvention.)
> I'm not exactly sure what the right approach is supposed to be here,
> though. Suggestions?
Fix interfere.
--
((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay:
http://barzilay.org/ Maze is Life!