[plt-scheme] Help with a macro
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