[racket] Strange macro behavior with set! and syntax-local-introduce

From: Robby Findler (robby at eecs.northwestern.edu)
Date: Fri May 16 13:47:29 EDT 2014

I agree.

Thanks,
Robby

On Fri, May 16, 2014 at 11:37 AM, Matthew Flatt <mflatt at cs.utah.edu> wrote:
> At Fri, 16 May 2014 08:59:12 -0500, Robby Findler wrote:
>> I think I'm still missing a step here because changing the set! in
>> funny-set! to a reference makes the "is it an expression" sensitivity
>> go away (the reference works in both cases), but the explanation would
>> seem to still apply.
>
> That's puzzling...
>
>> Is there something special about set!s that is
>> contributing here?
>
> It seems that I had made the corner smaller (and even more awkward?)
> with an extra check for variable references in the special case that
> the variable is bound in the enclosing module. I can put the same check
> in place for `set!`; I'm not sure that's a good idea, since it's not a
> general repair, but probably it's better for `set!` to be consistent
> with variable reference.
>
>
>> On Fri, May 16, 2014 at 1:47 AM, Matthew Flatt <mflatt at cs.utah.edu> wrote:
>> > You're running into an awkward corner of the macro system. The corner
>> > has to do identifiers that are produced by `generate-temporary` and
>> > then defined within a module.
>> >
>> > An identifier produced by `generate-temporary` doesn't claim to be from
>> > any particular context. If such identifier is defined within a module
>> > M, how do other uses of the identifier know to refer to the definition
>> > of M (since the identifier by itself doesn't claim to be from M)?
>> >
>> > The answer is that the definition marks the identifier as "introduced
>> > by a macro into M". It also marks each top-level form in a module with
>> > the "introduced to module M" context after expansion. The "top-level"
>> > modifier on "form" is crucial here.
>> >
>> > When you wrap `(funny-set! 3)` with `(void ...)`, then you delay its
>> > expansion until after all top-level forms are expanded. That is,
>> > `funny-set!` is not long a top-level form within the module.
>> >
>> >  * If there's no `void`, then the `funny-set!` form is expanded, and
>> >    the introduced variable is marked with the "introduced to M"
>> >    context, and that matches up with the definition.
>> >
>> >  * If there's a `void`, then the `(void (funny-set! 3))` form as a
>> >    whole is marked with "introduced into M", but the `funny-set!` macro
>> >    is not expanded, yet. Later, when `funny-set!` is expanded as a
>> >    nested expression, then the introduced identifier is *not* marked
>> >    with "introduced to M", and so it doesn't match up with the
>> >    definition.
>> >
>> > I'm itching to try a new macro expander that I think would solve this
>> > problem, and maybe that will happen sometime this year.
>> >
>> > Meanwhile, I would write `make-funny-set` as
>> >
>> >  (define-syntax (make-funny-set! stx)
>> >    (syntax-parse stx
>> >      [(_ v)
>> >       (define unmarked (generate-temporary))
>> >       #`(begin
>> >           (begin-for-syntax (set! funny (quote-syntax #,unmarked)))
>> >           (define #,unmarked v))]))
>> >
>> > and also
>> >
>> >  (define-syntax (funny-set! stx)
>> >    (syntax-parse stx
>> >      [(_ v)
>> >       #`(set! #,funny v)]))
>> >
>> > By expanding into `begin-for-syntax`, you get the macro expander to
>> > help you a little more: you don't have to use `syntax-local-introduce`.
>> > Also, you also avoid the awkward corner, because the still-quoted
>> > identifier is visible to the macro expander (and therefore available
>> > for the "introduced to M" mark) instead of sitting in a side channel.
>> >
>> > At Thu, 15 May 2014 16:25:47 -0500, Spencer Florence wrote:
>> >> I'm attempting to write a macro which introduces a new id, then another
>> >> macro that set!s that id.
>> >> Example:
>> >>
>> >> #lang racket
>> >> (require (for-syntax syntax/parse racket/syntax))
>> >> (define-for-syntax funny #f)
>> >> (define-syntax (make-funny-set! stx)
>> >>   (syntax-parse stx
>> >>     [(_ v)
>> >>      (define unmarked (generate-temporary))
>> >>      (set! funny (syntax-local-introduce unmarked))
>> >>      #`(define #,unmarked v)]))
>> >> (define-syntax (funny-ref stx)
>> >>   (syntax-parse stx
>> >>     [(_)
>> >>      funny]))
>> >> (define-syntax (funny-set! stx)
>> >>   (syntax-parse stx
>> >>     [(_ v)
>> >>      #`(set! #,(syntax-local-introduce funny) v)]))
>> >>
>> >> (make-funny-set! 2)
>> >> (funny-set! 3)
>> >> (funny-ref)
>> >>
>> >> This program works as I expect, evaluating to 3. However if I change
>> >> (funny-set! 3) to (void (funny-set! 3)) I get the error: "set!: unbound
>> >> identifier in module in: g1"
>> >>
>> >> I do not get this error if I change (funny-ref) to (void (funny-ref)).
>> >>
>> >> If I look at the expansion of the (void (funny-set! 3)) program in
>> >> drracket's macro stepper the the g1 in (define g1 2) and the g1 in (void
>> >> (set! g1 3)) have the same color.
>> >>
>> >> To keep on with the strange, if I change the #,(syntax-local-introduce
>> >> funny) inside of funny-set! to #,funny inside the behavior of all programs
>> >> remains the same.
>> >>
>> >> Could someone explain whats going on?
>> >>
>> >> --Spencer
>> >> ____________________
>> >>   Racket Users list:
>> >>   http://lists.racket-lang.org/users
>> > ____________________
>> >   Racket Users list:
>> >   http://lists.racket-lang.org/users

Posted on the users mailing list.