On Apr 27, 2016, at 2:38 PM, Matthew Butterick <m...@mbtype.com> wrote: > I suppose I'm wondering if a) this "cooperation" among macros I'm enjoying is > simply a lucky side effect of both being in the same module and thus sharing > a 'local' environment into which the identifier is being 'introduced', and if > so b) there's a better way to go about this than `syntax-local-introduce`.
For future mailing-list spelunkers, this is what I was able to infer by looking at the scope sets of introduced identifiers and thinking about the general rule that applies to resolving identifiers with scope sets. Maybe I've got it all wrong! But I tried. "More generally, we can define binding based on subsets: A reference’s binding is found as one whose set of scopes is [the largest] subset of the reference’s own scopes (in addition to having the same symbolic name)." (Flatt, Binding as Sets of Scopes) If you want to see the scope set of an identifier, use `(hash-ref (syntax-debug-info id 'context))`. SS1: Suppose "my-macros.rkt" has an ordinary `(define x ...)` . The scope set for that `x` looks like: > '(#(28013923 module) #(28013924 module my-macros 0)) SS2: Suppose a macro within "my-macros.rkt" defines `x`. The scope set for that `x` looks like: > '(#(1 top) #(28011205 top top4358309 0) #(28013923 module) #(28013924 module > my-macros 0) #(28015891 macro)) SS3: Suppose a different macro within "my-macros.rkt" defines `x`, this time with `syntax-local-introduce`. The scope set for that `x` looks like: > '(#(1 top) #(28011205 top top4358309 0) #(28013923 module) #(28013924 module > my-macros 0)) Thus, compared to SS2, `syntax-local-introduce` has the effect of removing the last scope. SS4: Now consider a macro in "my-macros.rkt" that invokes `x`. The scope set for that `x` looks like: > '(#(1 top) #(28011205 top top4358309 0) #(28013923 module) #(28013924 module > my-macros 0) #(28015902 macro)) So if you put all these macros in one expression at the use site, what happens? (let () ;; SS1 is the hygienic `x` hanging out back in "my-macros.rkt" (macro-that-defines-x) ; aka SS2 (macro-that-defines-x-with-syntax-local-introduce) ; aka SS3 (macro-that-invokes-x)) ; aka SS4 By the Flatt Rule of Scope Sets, the `x` in the `macro-that-invokes-x`, that carries scope set SS4, will resolve to a binding that has the same name & the largest scope subset. Both SS1 and SS3 meet these criteria. But because SS3 — the identifier created by `syntax-local-introduce` — has the larger subset, it takes precedence over SS1 (the basic hygienic `define` back in "my-macros.rkt"). Thus the `syntax-local-introduce` identifier overrides the hygienic binding. EPILOGUE While `syntax-local-introduce` now makes more sense to me, I still find it weird. Because is `syntax-local-introduce` hygienic? Well it doesn't seem UN-hygienic, inasmuch as it's not creating identifiers at the calling site. But by pruning the last scope, `syntax-local-introduce` is making an identifier appear as if it belongs to strange penumbral area of the macro-definition site that isn't otherwise accessible. Semihygienic? More generally, it makes me wonder if scope sets themselves erode the notion of hygiene. Before, syntax contexts could be categorized as either being on the macro-definition side (hygienic) or the use side (nonhygienic), because they were conceptually tied to to "natural" lexical contexts in the source code. So, do scope sets necessarily create a more nuanced model? As `syntax-local-introduce` suggests, by using set operations, you can create synthetic lexical contexts. Maybe manipulating those sets directly — rather than through proxies like `syntax-local-introduce` — could be interesting? Likewise adding scopes to syntax objects that have no purpose other than to create the possibility of matching these synthetic contexts? -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.