Re: [racket-dev] Enhancement to the syntax system?
The difference between (define-for-syntax (fx x) #`(let ((x 0)) #,x)) ^---* and (define-for-syntax (fy y) #`(let ((x 0)) #,y)) * is that the `x` of `(let ((x ...)))` gets lexical context from the `x` argument to `fx`, but there's no such binding for the context of `x` within `fy`. The `x` in the `let` form generated by `fx` will only bind others `x`s that also have the same context. So, it won't bind `x` passed as (fx #'x) since that `x` doesn't have the argument of `fx` in its context. To get an `x` from the inside of `fx`, you could pull it out of the result of a call to `fx`: (syntax-case (fx #'y) () [(let ([var rhs]) body) ;; #'var is `x` from the inside of `fx`: (fx #'var)]) A case could be made that the `x` of `(let ((x ...)))` shouldn't have anything to do with the `x` argument of `fx`, because they live in different phases. There's a trade-off between allowing different bindings in different phases and providing a more useful error message when a binding is accidentally used in the wrong phase. At Tue, 13 May 2014 14:44:43 +0200, "Marijn Schouten (hkBst)" wrote: > I realize this is a very old thread, but the behavior of Racket has not > changed since. > > On 03-05-14 23:29, Marijn Schouten (hkBst) wrote: > > On 07/10/2012 05:03 PM, Matthew Flatt wrote: > >> At Tue, 10 Jul 2012 10:51:57 -0400, Eli Barzilay wrote: > >>> 20 minutes ago, Marijn wrote: > > It seems to me that both these results cannot be correct > simultaneously, but I'll await the experts' opinion on that. > >>> > >>> This does look weird: > >>> > >>> #lang racket > >>> (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) > >>> (define-syntax (m stx) > >>> (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) > >>> (m) > >>> > >>> evaluates to 1, but if I change the first two "stx" names into "x" > >>> *or* if I change the argument name for the macro to "x", then it > >>> returns 2. > >> > >> It's natural --- but not correct --- to think that #` is responsible > >> for hygiene, in which case `(f #'x)' should keep the given `x' separate > >> from the `let'-bound `x' in the result. > >> > >> Instead, hygiene is the responsibility of macro invocation, and > >> > >> #`(let ([x 1]) #,#'x) > >> > >> is simply the same as > >> > >> #`(let ([x 1]) x) > >> > >> and so > >> > >> (f #'x) > >> > >> above is equivalent to > >> > >> #`(let ([x 1]) x) > > > > IIUC then you're saying that also all the following (fx #'x) and (fy > > #'x) are equivalent to #`(let ((x 0)) x), but if you run this code then > > you will get a 0 result only for (myy), contrary to what I would expect > > based on the above explanation. > > > > #lang racket > > > > (define-for-syntax (fx x) #`(let ((x 0)) #,x)) > > (define-for-syntax (fy y) #`(let ((x 0)) #,y)) > > > > (define-syntax (mxx x) > > (syntax-case x () ((_) #`(let ((x 99)) #,(fx #'x) > > > > (define-syntax (mxy x) > > (syntax-case x () ((_) #`(let ((x 99)) #,(fy #'x) > > > > (define-syntax (myx y) > > (syntax-case y () ((_) #`(let ((x 99)) #,(fx #'x) > > > > (define-syntax (myy y) > > (syntax-case y () ((_) #`(let ((x 99)) #,(fy #'x) > > > > (mxx) > > (mxy) > > (myx) > > (myy) > > > > ==> > > > > 99 > > 99 > > 99 > > 0 > > > > Marijn > > _ > > Racket Developers list: > > http://lists.racket-lang.org/dev > > _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
I realize this is a very old thread, but the behavior of Racket has not changed since. On 03-05-14 23:29, Marijn Schouten (hkBst) wrote: > On 07/10/2012 05:03 PM, Matthew Flatt wrote: >> At Tue, 10 Jul 2012 10:51:57 -0400, Eli Barzilay wrote: >>> 20 minutes ago, Marijn wrote: It seems to me that both these results cannot be correct simultaneously, but I'll await the experts' opinion on that. >>> >>> This does look weird: >>> >>> #lang racket >>> (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) >>> (define-syntax (m stx) >>> (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) >>> (m) >>> >>> evaluates to 1, but if I change the first two "stx" names into "x" >>> *or* if I change the argument name for the macro to "x", then it >>> returns 2. >> >> It's natural --- but not correct --- to think that #` is responsible >> for hygiene, in which case `(f #'x)' should keep the given `x' separate >> from the `let'-bound `x' in the result. >> >> Instead, hygiene is the responsibility of macro invocation, and >> >> #`(let ([x 1]) #,#'x) >> >> is simply the same as >> >> #`(let ([x 1]) x) >> >> and so >> >> (f #'x) >> >> above is equivalent to >> >> #`(let ([x 1]) x) > > IIUC then you're saying that also all the following (fx #'x) and (fy > #'x) are equivalent to #`(let ((x 0)) x), but if you run this code then > you will get a 0 result only for (myy), contrary to what I would expect > based on the above explanation. > > #lang racket > > (define-for-syntax (fx x) #`(let ((x 0)) #,x)) > (define-for-syntax (fy y) #`(let ((x 0)) #,y)) > > (define-syntax (mxx x) > (syntax-case x () ((_) #`(let ((x 99)) #,(fx #'x) > > (define-syntax (mxy x) > (syntax-case x () ((_) #`(let ((x 99)) #,(fy #'x) > > (define-syntax (myx y) > (syntax-case y () ((_) #`(let ((x 99)) #,(fx #'x) > > (define-syntax (myy y) > (syntax-case y () ((_) #`(let ((x 99)) #,(fy #'x) > > (mxx) > (mxy) > (myx) > (myy) > > ==> > > 99 > 99 > 99 > 0 > > Marijn > _ > Racket Developers list: > http://lists.racket-lang.org/dev > _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
On 07/10/2012 05:03 PM, Matthew Flatt wrote: > At Tue, 10 Jul 2012 10:51:57 -0400, Eli Barzilay wrote: >> 20 minutes ago, Marijn wrote: >>> >>> It seems to me that both these results cannot be correct >>> simultaneously, but I'll await the experts' opinion on that. >> >> This does look weird: >> >> #lang racket >> (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) >> (define-syntax (m stx) >> (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) >> (m) >> >> evaluates to 1, but if I change the first two "stx" names into "x" >> *or* if I change the argument name for the macro to "x", then it >> returns 2. > > It's natural --- but not correct --- to think that #` is responsible > for hygiene, in which case `(f #'x)' should keep the given `x' separate > from the `let'-bound `x' in the result. > > Instead, hygiene is the responsibility of macro invocation, and > > #`(let ([x 1]) #,#'x) > > is simply the same as > > #`(let ([x 1]) x) > > and so > > (f #'x) > > above is equivalent to > > #`(let ([x 1]) x) IIUC then you're saying that also all the following (fx #'x) and (fy #'x) are equivalent to #`(let ((x 0)) x), but if you run this code then you will get a 0 result only for (myy), contrary to what I would expect based on the above explanation. #lang racket (define-for-syntax (fx x) #`(let ((x 0)) #,x)) (define-for-syntax (fy y) #`(let ((x 0)) #,y)) (define-syntax (mxx x) (syntax-case x () ((_) #`(let ((x 99)) #,(fx #'x) (define-syntax (mxy x) (syntax-case x () ((_) #`(let ((x 99)) #,(fy #'x) (define-syntax (myx y) (syntax-case y () ((_) #`(let ((x 99)) #,(fx #'x) (define-syntax (myy y) (syntax-case y () ((_) #`(let ((x 99)) #,(fy #'x) (mxx) (mxy) (myx) (myy) ==> 99 99 99 0 Marijn _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
The question I posed was If it's possible to use srfi-72 in guile or racket. It is indeed a wish of mine that it's implemented because it will most certainly help me write beutiful code because that srfi cater to the coding style Ichoose to have. Without it one most certainly need to use with-syntax to introduce the bindings in order to be safe and avoid a multiple of possible syntactic loopholes as can see if you read André van Tonder's http://srfi.schemers.org/srfi-72/srfi-72.html Anyway one does not need to change psyntax in order to come close to that system. A more hacky way is to use code like the one at the end of this document. It's a reiteration and improvement on a previous version. So with this hack I can write, - (use-modules (syntax unquote++)) (define (f x y) #`(let ((x 1)) (+ #,x #,y))) (define (h y) ##`(let ((x 2)) #.((x) (f x y (define-syntax g (lambda (x) (syntax-case x () ((_ y) ##`(let ((x y)) #.((x) (h x))) scheme@(guile-user)> (g 3) $2 = 5 -- In racket, (define-for-syntax (f x y) #`(let ((x 1)) (+ #,x #,y))) (define-for-syntax (h y) #`(let ((x 2)) #,(f #'x y)) (define-syntax (g stx) (syntax-case stx () ((_ y) #`(let ((x y)) #,(h #'x) > (g 3) 4 -- This shows that it was just luck previously when racket produced the correct (for my intention) answer. with srfi-72 a correct answer would have been produced. Without srfi-72 I will then move over to use ##` and #. in my code because it will be easy to transfer later on if when srfi-72 is available in some form. /Stefan (define-module (syntax unquote++) #:export (quasisyntax++ insyntax)) (define *s* (make-fluid '())) (define *t* (make-fluid '())) (define table (make-weak-key-hash-table)) (define (add-lambda lam) (let* ((id (gensym "id")) (x (datum->syntax #'table id))) (hashq-set! table id lam) x)) (define (plexer x . l) (let ((lam (hashq-ref table x))) (apply lam l))) (define (parse stx x) (syntax-case x (unsyntax insyntax unsyntax-splicing) ((unsyntax . _) x) ((unsyntax-splicing . _) x) ((insyntax ((p ...) c ...)) (with-syntax ((g (datum->syntax stx (gensym "g"))) (i (datum->syntax stx (gensym "i" (fluid-set! *s* (cons #'(g (lambda (x) (syntax-case x () ((_ p ...) (plexer 'i #'p ...) (fluid-ref *s*))) (fluid-set! *t* (cons #'(i (add-lambda (lambda (p ...) (begin c ... (fluid-ref *t*))) #'(g p ...))) ((x . l) (cons (parse stx #'x) (parse stx #'l))) (x #'x))) (define-syntax quasisyntax++ (lambda (x) (syntax-case x () ((_ y) (begin (fluid-set! *s* '()) (fluid-set! *t* '()) (with-syntax ((statement (parse x #'y)) (lets (fluid-ref *s*)) (withs (fluid-ref *t*))) #'(with-syntax withs #`(let-syntax lets statement (define (rg ch stream) (let ((x (read-char stream))) (cond ((eqv? x #\`) `(quasisyntax++ ,(read stream))) (#t (error "Wrong format of # reader extension") (define (rg. ch stream) `(insyntax ,(read stream))) (read-hash-extend #\# rg) (read-hash-extend #\. rg.) _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
An hour and a half ago, Matthew Flatt wrote: > > It's natural --- but not correct --- to think that #` is responsible > for hygiene, in which case `(f #'x)' should keep the given `x' > separate from the `let'-bound `x' in the result. > > Instead, hygiene is the responsibility of macro invocation, and > > #`(let ([x 1]) #,#'x) > > is simply the same as > > #`(let ([x 1]) x) > > and so > > (f #'x) > > above is equivalent to > > #`(let ([x 1]) x) > > > If you change the example to > > #lang racket > (begin-for-syntax > (define-syntax-rule (f body) > #`(let ([x 1]) body))) > (define-syntax (m stx) >(with-syntax ([zz (f x)]) #`(let ([x 2]) zz))) > (m) > > so that `f' is used as a macro instead of a function, then you get 2, > since the macro-expansion of `(f x)' keeps the `x's separate. Yeah, I think that this kind of confusion is there, but it's easy (at least "relatively easy") to build a mental model of how things work and avoid such problems -- but the tricky bit here is that things break when the `stx' is renamed to `x'. 50 minutes ago, Ryan Culpepper wrote: > > The difference between > > (define-for-syntax (f1 stx) #`(let ([x 1]) #,stx) > > and > > (define-for-syntax (f2 x) #`(let ([x 1]) #,x) > > is that the 'x' in the template in the 'f2' is not bound-identifier=? to > the 'x' that appears in the template of 'm', because it has a rename > wrap due to the 'x' formal parameter of 'f2'. That is, 'f2' behaves > essentially the same as > > (define-for-syntax (f2* x*) #`(let ([x* 1]) #,x*) Yeah, I think that this is the more confusing bit. (I suspected something along this line, but didn't have time to figure out a good explanation...) So I think that the real confusion is actually a combination of what Matthew pointed at in the above and this one, making the result trickier than both. In other words, I *think* that the effect of the transformer's argument name makes it looks like the #` *was* responsible for hygiene when it's actually just this point that makes it happen... (This is all "IIUC".) > A likely mistake is to think that the wrap generated by the 'x' in > 'f2' doesn't count because it happens before we get around to the > "real" macro expansion that you care about. But that's not the case > (at least in Racket). > > A good rule of thumb is to never use local variable names in your > macro implementation (including compile-time auxiliary functions) > that also appear in the macro's template (including etc). Yeah, and a subtle lesson here is that `stx' is a useful convention. (I think that `x' is common in some guile code, so this would be a point for them.) > A related error is the "identifier used out of context" error that > you get from, eg, > > (define-syntax (m stx) >(let ([+ *]) > #'(+ 1 2))) > (m) ;; => identifier used out of context in: + > > The binding of '+' in the macro changes the meaning of the '+' in > the template, even though the bindings exist at different > phases. You could perhaps avoid this issue by changing the hygiene > algorithm by adding a phase level to rename wraps and skipping > different-phase rename wraps during resolution. I'm not sure if this > is a good idea or if anyone has tried it. (And this is what Matthew's last example gets by changing `f' to a macro, right? Also, Stefan posted a related message to the scheme-reports list where he imlpemented some new #. thing which is (roughly speaking) something that expands to a `let-syntax' and therefore tries to do the same.) In any case, it would be nice if the original example I posted (which is a variant of what was discussed originally) would throw an error instead of looking right in a way that is wrong... -- ((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay: http://barzilay.org/ Maze is Life! _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
Hi, Matthew Flatt skribis: > It's natural --- but not correct --- to think that #` is responsible > for hygiene, in which case `(f #'x)' should keep the given `x' separate > from the `let'-bound `x' in the result. [...] > If you change the example to > > #lang racket > (begin-for-syntax > (define-syntax-rule (f body) > #`(let ([x 1]) body))) > (define-syntax (m stx) >(with-syntax ([zz (f x)]) #`(let ([x 2]) zz))) > (m) > > so that `f' is used as a macro instead of a function, then you get 2, > since the macro-expansion of `(f x)' keeps the `x's separate. Interesting. Thanks for the clarification and examples. Ludo’. _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
On 07/10/2012 10:51 AM, Eli Barzilay wrote: 20 minutes ago, Marijn wrote: It seems to me that both these results cannot be correct simultaneously, but I'll await the experts' opinion on that. This does look weird: #lang racket (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) (define-syntax (m stx) (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) (m) evaluates to 1, but if I change the first two "stx" names into "x" *or* if I change the argument name for the macro to "x", then it returns 2. The difference between (define-for-syntax (f1 stx) #`(let ([x 1]) #,stx) and (define-for-syntax (f2 x) #`(let ([x 1]) #,x) is that the 'x' in the template in the 'f2' is not bound-identifier=? to the 'x' that appears in the template of 'm', because it has a rename wrap due to the 'x' formal parameter of 'f2'. That is, 'f2' behaves essentially the same as (define-for-syntax (f2* x*) #`(let ([x* 1]) #,x*) A likely mistake is to think that the wrap generated by the 'x' in 'f2' doesn't count because it happens before we get around to the "real" macro expansion that you care about. But that's not the case (at least in Racket). A good rule of thumb is to never use local variable names in your macro implementation (including compile-time auxiliary functions) that also appear in the macro's template (including etc). A related error is the "identifier used out of context" error that you get from, eg, (define-syntax (m stx) (let ([+ *]) #'(+ 1 2))) (m) ;; => identifier used out of context in: + The binding of '+' in the macro changes the meaning of the '+' in the template, even though the bindings exist at different phases. You could perhaps avoid this issue by changing the hygiene algorithm by adding a phase level to rename wraps and skipping different-phase rename wraps during resolution. I'm not sure if this is a good idea or if anyone has tried it. Ryan _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
samth made a pointer to http://srfi.schemers.org/srfi-72/srfi-72.html It does not look like guile racket etc. have implemented this yet. Am I wrong? This is precisely what I'm after! On Tue, Jul 10, 2012 at 5:26 PM, Ludovic Courtès wrote: > Hi, > > Matthew Flatt skribis: > > > It's natural --- but not correct --- to think that #` is responsible > > for hygiene, in which case `(f #'x)' should keep the given `x' separate > > from the `let'-bound `x' in the result. > > [...] > > > If you change the example to > > > > #lang racket > > (begin-for-syntax > > (define-syntax-rule (f body) > > #`(let ([x 1]) body))) > > (define-syntax (m stx) > >(with-syntax ([zz (f x)]) #`(let ([x 2]) zz))) > > (m) > > > > so that `f' is used as a macro instead of a function, then you get 2, > > since the macro-expansion of `(f x)' keeps the `x's separate. > > Interesting. Thanks for the clarification and examples. > > Ludo’. > > _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
At Tue, 10 Jul 2012 10:51:57 -0400, Eli Barzilay wrote: > 20 minutes ago, Marijn wrote: > > > > It seems to me that both these results cannot be correct > > simultaneously, but I'll await the experts' opinion on that. > > This does look weird: > > #lang racket > (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) > (define-syntax (m stx) > (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) > (m) > > evaluates to 1, but if I change the first two "stx" names into "x" > *or* if I change the argument name for the macro to "x", then it > returns 2. It's natural --- but not correct --- to think that #` is responsible for hygiene, in which case `(f #'x)' should keep the given `x' separate from the `let'-bound `x' in the result. Instead, hygiene is the responsibility of macro invocation, and #`(let ([x 1]) #,#'x) is simply the same as #`(let ([x 1]) x) and so (f #'x) above is equivalent to #`(let ([x 1]) x) If you change the example to #lang racket (begin-for-syntax (define-syntax-rule (f body) #`(let ([x 1]) body))) (define-syntax (m stx) (with-syntax ([zz (f x)]) #`(let ([x 2]) zz))) (m) so that `f' is used as a macro instead of a function, then you get 2, since the macro-expansion of `(f x)' keeps the `x's separate. _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
20 minutes ago, Marijn wrote: > > It seems to me that both these results cannot be correct > simultaneously, but I'll await the experts' opinion on that. This does look weird: #lang racket (define-for-syntax (f stx) #`(let ([x 1]) #,stx)) (define-syntax (m stx) (with-syntax ([zz (f #'x)]) #`(let ([x 2]) zz))) (m) evaluates to 1, but if I change the first two "stx" names into "x" *or* if I change the argument name for the macro to "x", then it returns 2. -- ((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay: http://barzilay.org/ Maze is Life! _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Enhancement to the syntax system?
-BEGIN PGP SIGNED MESSAGE- Hash: SHA1 On 10-07-12 15:35, Stefan Israelsson Tampe wrote: > I did miss something when trying in racket, it's a psyntax bug! I'm glad you're finally looking harder at the Racket behavior. > racket: (define-for-syntax (f x) #`(let ((x 1)) #,x)) >> (define-syntax (g x) (syntax-case x ()((_ y) #`(let ((x y)) #,(f >> #'x > ) >> (g 4) > 4 This code produces the same error for me with Racket which I reported earlier, but I seem to have been using a non-release version. So, switching to 5.2.1, I can report that indeed the code #lang racket (define-for-syntax (f x) #`(let ((x 1)) #,x)) (define-syntax (g x) (syntax-case x () ((_ y) #`(let ((x y)) #,(f #'x) (g 4) produces 4, which makes sense to me. However when I retry my earlier example #lang racket (define-for-syntax (f stx) #`(let ((x 1)) #,stx)) (define-syntax (m stx) (syntax-case stx () ((_) #`(let ((x 2)) #,(f #'x))) )) (m) it now produces 1 instead of the expected 2. It seems to me that both these results cannot be correct simultaneously, but I'll await the experts' opinion on that. Marijn -BEGIN PGP SIGNATURE- Version: GnuPG v2.0.19 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAk/8PXIACgkQp/VmCx0OL2zdfQCeOVJz/ECKExThGPJoqvRY3E41 gr0An0vSsuXK2B7xi0WT0LuzvVvhUbcy =04xS -END PGP SIGNATURE- _ Racket Developers list: http://lists.racket-lang.org/dev