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

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Fri May 16 12:37:06 EDT 2014

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.