nested quote-syntax

41 views
Skip to first unread message

David Vanderson

unread,
May 16, 2017, 11:23:19 AM5/16/17
to d...@racket-lang.org
I'm working through "Let's Build a Hygienic Macro Expander!", which is great, but I'm confused about nested quote-syntax:

#lang racket
(require (for-syntax racket))

(define x 5)
(begin-for-syntax
  (define-syntax m2
    (lambda (stx)
      #'#'x)))
;(let ()
  (define-syntax m
    (lambda (stx)
      #`(let ((x 6))
          #,(m2))))
  (m)
;)

I think the x inside #'#'x should keep its original lexical context (prints 5).  But maybe quote-syntax only keeps one layer of context, so the inside #'x gets the context where m2 was called (prints 6)?

In DrRacket 6.9, I get 6 as shown, and uncommenting that extra let () changes it to output 5, which seems fishy.  Other ways I can think of to write it all return 6.

Can anyone enlighten me?

Thanks,
Dave

Matthew Flatt

unread,
May 17, 2017, 10:36:06 AM5/17/17
to David Vanderson, d...@racket-lang.org
Hi Dave,

The current and old macro expanders differ on this example. I think you
can make a case for either result, but this kind of example bothers
Michael Ballantyne and William Hatch enough (on the grounds that it
should behave the old way) that they have been exploring different
notions of scope to get back the old behavior.

I don't think we've seen this specific example before, which uses a
macro defined at phase 1 to more simply provoke a difference in
expansions. Maybe Michael and William remember differently, though.

In the set of scopes model, the `x` introduced as a binding to 6 has no
extra scopes relative to the `x` introduced by the expansion of `(m2)`,
so the reference is captured by the binding. When you add `let`,
however, it introduces a scope that is not on the `x` from the
expansion of `(m2)`, so the binding to 6 doesn't capture in that case.

The mark-and-rename model of expansion, in contrast, effectively keeps
track of the order. The expansion of `(m2)` has an extra mark that
makes it ineligible for renaming by the binding of `x` to 6, even
without an extra `let` layer, so that's why the expansion of `(m2)`
produces a reference to the binding to 5.

I'm inclined to blame the discrepancy on definition contexts: the
example defines and uses `x`, `m2, and `m` all in the same definition
context, and that definition-and-use cycle creates a kind of ambiguity.
The differences that we see between the mark-and-rename and
set-of-scopes expanders are generally related to definition contexts
like that, and it's surely related to complexity in the old model [1]
that we're trying to avoid with scope sets.

Matthew

[1] Section 3.8 of "Macros that Work Together"
Reply all
Reply to author
Forward
0 new messages