[racket-dev] Enhancement to the syntax system?

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Tue May 13 09:04:56 EDT 2014

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
> > 

Posted on the dev mailing list.