[racket] Strange macro behavior with set! and syntax-local-introduce
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