[plt-scheme] Introducing an Identifier into the Lexical Conte xt of a Macro Cal l

From: Williams, M. Douglas (M.DOUGLAS.WILLIAMS at saic.com)
Date: Wed Oct 13 10:35:02 EDT 2004

The with-syntax is more appealing.  In my case, I am generating a let to
introduce a variable around a body of code from the macro call.  I assume
that generating the identifier in the context of the body would address the
macros expanding to non-hygienic macros issue (at least in most cases).

Is there a good example anywhere of similar code in PLT Scheme?  Generating
a 'self' variable for class methods would be an example that comes to mind.

Doug

P.S.  And here I thought I knew LISP (and, therefore, Scheme) already.  :^)

> Jens Axel Søgaard wrote:
> 
> 
> Williams, M. Douglas wrote:
> > The PLT Scheme Reference Manual contains the following example in
> > section 12.2.1:
> 
> > ---
> >
> > Another reason to use syntax-case is to implement ``non-hygienic''
> macros
> > that introduce capturing identifiers:
> >
> > (define-syntax (if-it stx)
> >   (syntax-case stx ()
> >     [(src-if-it test then else)
> >      (syntax-case (datum->syntax-object (syntax src-if-it) 'it) ()
> >        [it (syntax (let ([it test]) (if it then else)))])])))
> > (if-it (memq 'b '(a b c)) it 'nope) ; => '(b c)
> >
> > ---
> >
> > Is this the best way to introduce an identifier ('it' in this case) into
> the
> > lexical context of the macro's use?
> 
> 
> Later in the manual there is an example I like better:
> 
>   (define-syntax (if-it stx)
>     (syntax-case stx ()
>       [(src-if-it test then else)
>        (with-syntax ([it (datum->syntax-object (syntax src-if-it) 'it)])
>          (syntax (let ([it test]) (if it then else))))])))
> 
> Right after that example comes:
> 
> --- quote start ---
> 
> Macros that expand to non-hygienic macros rarely work as intended. For
> example:
> 
> (define-syntax (cond-it stx)
>   (syntax-case stx ()
>     [(_ (test body) . rest)
>      (syntax (if-it test body (cond-it . rest)))]
>     [(_) (syntax (void))]))
> (cond-it [(memq 'b '(a b c)) it] [#t 'nope]) ; => undefined variable it
> 
> The problem is that cond-it introduces if-it (hygienically), so
> cond-it effectively introduces it (hygienically), which doesn't bind
> it in the source use of cond-it. In general, the solution is to avoid
> macros that expand to uses of non-hygienic macros. (32)
> 
> (32) In this particular case, Shriram Krishnamurthi points out
> changing if-it to use (datum->syntax-object (syntax test) 'it) solves
> the problem in a sensible way.
> 
> --- quote ends ---
> 
> Suppose for arguments sake that if-it were provided by an external
> module, and that you couldn't change it. How should cond-it be
> defined?
> 
> The only working solution I could find were:
> 
>   (define-syntax (cond-it stx)
>     (syntax-case stx ()
>       [(src-cond-it (test body) . rest)
>        (let ((if-it-transformer (syntax-local-value #'if-it))
>              (if-it-stx         (datum->syntax-object (syntax src-cond-it)
> 'if-it)))
>          (if-it-transformer #`(#,if-it-stx test body (cond-it . rest))))]
>       [(_) (syntax (void))]))
> 
> Is there a simpler solution?
> 
> 
> Here is the entire example with tests (that breaks with all the simpler
> attempts I tried):
> 
> 
> (module if-it mzscheme
>   (provide if-it)
> 
>   (define-syntax (if-it stx)
>     (syntax-case stx ()
>       [(src-if-it test then else)
>        (with-syntax ([it (datum->syntax-object (syntax src-if-it) 'it)])
>          (syntax (let ([it test]) (if it then else))))])))
> 
> (module cond-it mzscheme
>   (provide cond-it)
>   (require if-it)
>   ;(require-for-syntax if-it)
> 
>   (define-syntax (cond-it stx)
>     (syntax-case stx ()
>       [(src-cond-it (test body) . rest)
>        (let ((if-it-transformer (syntax-local-value #'if-it))
>              (if-it-stx         (datum->syntax-object (syntax src-cond-it)
> 'if-it)))
>          (if-it-transformer #`(#,if-it-stx test body (cond-it . rest))))]
>       [(_) (syntax (void))])))
> 
> (require cond-it)
> 
> (cond-it
>  [#t it]
>  [#t 'nope])
> 
> (let ((it 42))
>   (cond-it
>    [#t it]
>    [#t 'nope]))
> 
> (let ((if-it 43))
>   (let ((it 42))
>     (cond-it
>      [#t it]
>      [#t 'nope])))
> 
> (cond-it ((memq 'b '(a b c))
>             (let ((it0 it))
>               (if-it (memq 'y '(x y z))
>                      (values it0 it)
>                      'nope0)))
>            (#t 'nope1))
> 
> (require if-it)
> 
> (cond-it
>  [#t it]
>  [#t 'nope])
> 
> (let ((it 42))
>   (cond-it
>    [#t it]
>    [#t 'nope]))
> 
> (let ((if-it 43))
>   (let ((it 42))
>     (cond-it
>      [#t it]
>      [#t 'nope])))
> 
> (cond-it ((memq 'b '(a b c))
>             (let ((it0 it))
>               (if-it (memq 'y '(x y z)) (values it0 it) 'nope0)))
>            (#t 'nope1))
> 
> 
> --
> Jens Axel Søgaard
> 



Posted on the users mailing list.