> On Mar 4, 2018, at 3:40 PM, Alexis King <lexi.lam...@gmail.com> wrote: > > Apologies in advance for both the inflammatory subject and yet another > overly long email to this list.
I wouldn’t call this inflammatory. It might be considered a bug report. Thanks for the thorough analysis. Would you be in a position to add syntax classes to Michael Adam’s model of hygiene (I know that it doesn’t support define, but I think one can think of define-syntax-class as (let-for-syntax ((a (syntax-class …)). One could also add it to Matthew’s model and see what is doable there. If the two agree that syntax-generating attributes can be made hygienic, good. If not, one might wish to consider telling people (forcing?) to produce values that are then fed into auxiliary macros. I am looking forward to Ryan’s response — Matthias > > I think anyone who knows me knows that I love syntax/parse — I think > it’s far and away one of Racket’s most wonderful features — but I’ve > long suspected it does not respect hygiene. Consider: > > #lang racket > (require syntax/parse/define) > > (define x #f) > > (begin-for-syntax > (define-syntax-class a > [pattern _ #:attr def #'(define x #t)]) > (define-syntax-class b > [pattern _ #:attr use #'x])) > > (define-simple-macro (m a:a b:b) > (begin a.def b.use)) > > (m 0 0) ; => #t > > This program produces #t from the reference to x on line 10. Considering > the natural lexical scope of the program, as it appears to a human > reader, there is no local definition of x in scope where #'x is written > on line 10, so it logically ought to refer to the top-level definition > of x on line 4, which would make the program produce #f. However, it > does not. Instead, it produces #t because it actually refers to the > definition of x written on line 8, which is assembled alongside the use > on line 13. > > While this behavior makes sense from the perspective of someone familiar > with the semantics of procedural macros, if taken from the point of view > of pattern-based systems, it seems to violate one of the essential > properties of a hygienic macro system. Namely, the macro system should > respect program scope. The above program does not. > > For those unfamiliar with the details, this behavior occurs because > syntax class uses are not treated like macro transformations. When a > macro is expanded, a fresh scope is attached to its expansion, but when > a syntax class is used, its syntax objects have no additional > introduction scope. One could argue this behavior is useful — sometimes > it is helpful to be able to assemble larger pieces of syntax from the > outputs of different syntax classes without needing to pass shared > identifiers as input to the classes — but it also causes problems. I > think I first ran into it when I was using a syntax class to generate a > series of definitions: > > #lang racket > (require syntax/parse/define) > > (begin-for-syntax > (define-syntax-class def-and-use > [pattern val:expr > #:attr x #'(begin > (define tmp (+ val 1)) > (displayln tmp))])) > > (define-simple-macro (m a:def-and-use ...) > (begin a.x ...)) > > (m 1 2 3) > > I would expect this program to print "2\n3\n4\n", but instead, it fails > to compile with an error: > > module: identifier already defined > in: tmp > > The multiple definitions of tmp are assembled alongside each other, and > since they all have the same scopes, they collide. A solution is to use > generate-temporary, but that is a little ugly. A solution that uses a > helper macro in place of the syntax class has no such problem: > > #lang racket > (require syntax/parse/define) > > (define-simple-macro (def-and-use val:expr) > (begin (define tmp (+ val 1)) > (displayln tmp))) > > (define-simple-macro (m a:expr ...) > (begin (def-and-use a) ...)) > > (m 1 2 3) > > There are arguments to be made that the existing behavior is not > unreasonable. Syntax classes behave like phase 1 functions, not macros. > If one desires macro-like behavior, it’s often possible to use a helper > macro instead of a syntax class. This is not always true, however; > sometimes syntax classes are used to generate syntax that will be > inserted into places where the macroexpander will not run (such as > binding positions), but one still needs to use generate-temporaries to > avoid duplicate bindings. > > There are some minor questions as to what the semantics of “hygienic” > syntax classes would be, since they accept arbitrary values as inputs > (in the case of parameterized syntax classes), not exclusively syntax > objects. They also have multiple outputs, some of which may not be > syntax-valued, so it’s not immediately obvious to me if performing the > same scope flipping that works for macros would produce the appropriate > result for syntax classes. > > Still, with all this context out of the way, my questions are > comparatively short: > > 1. Is this lack of hygiene well-known? I did not find anything in > Ryan’s dissertation that explicitly dealt with the question, but I > did not look very hard, and even if it isn’t explicitly mentioned > there, I imagine people have thought about it before. > > 2. Are there some fundamental, theoretical obstacles to making a > syntax class-like thing hygienic that I have not foreseen? Or would > it really be as simple as performing the usual scope-flipping that > macroexpansion already performs? > > 3. If it is possible, is the unhygienic nature of syntax classes > desirable frequently enough that it outweighs the benefits of > respecting hygiene? That seems unlikely to me, but maybe I have not > fully considered the problem. The semantics of syntax classes > cannot be changed now, of course, for backwards compatibility > reasons, but were that not a problem, would it make sense to make > them hygienic? If not, why not? > > Many thanks to all who managed to get to the end of this email, > Alexis > > -- > 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. -- 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.