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?
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?
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?
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?
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?
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 l...@gnu.org wrote: Hi, Matthew Flatt mfl...@cs.utah.edu 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?
Hi, Matthew Flatt mfl...@cs.utah.edu 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?
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?
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