[plt-scheme] Help with a macro

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Mon Oct 3 11:35:13 EDT 2005

At Mon, 3 Oct 2005 07:40:47 -0600, Evan Farrer wrote:
> (define-syntax (a-loop stx)
>   (syntax-case stx (for)
>                [(_ count body ...)
>                (with-syntax ([itid (datum->syntax-object (syntax count) 
> 'it)])     
>                             (let ([done (equal? 0 (syntax-object->datum 
> #'count))]
>                                         [next (sub1 (syntax-object->datum 
> #'count))])
>                               #`(let ([itid count])
>                                   #,(if done
>                                       #'(begin body ...)
>                                       #`(begin
>                                           body ...
>                                           (_ #,next body ...))))))]))

Expanding to a use of a non-hygienic macro is troublesome, because the
macro is going to grab some part of its input to give context to the
non-hygienic identifier. In this case, `a-loop' uses the context of
`count'.

On the first iteration, `count' has the context that you want. On the
second iteration, though, `count' has the context of `#,next', which is
not what you want.

Using `#,(datum->syntax-object (syntax count) next)' avoids the
problem, at least for your example, by transferring the context of the
original `count' to each `next'.

Better yet, avoid the non-hygienic identifier. Using a syntax
parameter, you can also avoid making code that uses `a-loop' supply an
identifier to bind. Instead you end up with a single `it' binding whose
meaning is adjusted by `a-loop':

 (require (lib "stxparam.ss"))

 (define-syntax-parameter it (lambda (stx)
                              (raise-syntax-error
                               #f
                               "only valid in `a-loop'"
                               stx)))

 (define-syntax (a-loop stx)
  (syntax-case stx (for)
    [(_ count body ...)
     (let ([done (equal? 0 (syntax-object->datum #'count))]
           [next (sub1 (syntax-object->datum #'count))])
       #`(let ([itid count])
           ;; Map `it' to `itid' for the body:
           (syntax-parameterize ([it (make-rename-transformer 
                                      (quote-syntax itid))])
             #,(if done
                   #'(begin body ...)
                   #`(begin
                       body ...
                       (_ #,next body ...))))))]))


 > (a-loop 9 (display it))
 9876543210
 > it
 it: only valid in `a-loop' in: it


Matthew



Posted on the users mailing list.