[racket] Another macro question: making a lexically binding keyword?
Let's say that I have the following toy iteration form for doing
something over a sequence of numbers:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang racket
(require racket/stxparam)
(define-syntax (ranged stx)
(syntax-case stx ()
[(_ n body ...)
#'(for ([k n])
(syntax-parameterize
([INDEX (make-rename-transformer #'k)])
(begin body ...)))]))
(define-syntax-parameter INDEX
(lambda (stx)
(raise-syntax-error #f "Used out of context of a ranged" stx)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
This provides INDEX as a way to get at the index used at the kth
iteration. For example:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> (ranged 5 (displayln INDEX))
0
1
2
3
4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
However, as Brian Mastenbrook notes, the use of syntax parameters here
can be troublesome because they don't necessarily work lexically. For
example, we might be devious and do this:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax (interfere stx)
(syntax-case stx ()
[(_ body ...)
#'(ranged 1 body ...)]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
The interfere macro here should be a no-op. However, the use of
'interfere' can interfere with the use of ranged. As a case in point:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> (ranged 5 (interfere (displayln INDEX)))
0
0
0
0
0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
and ideally, I would have liked to see this instead:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> (ranged 5 (interfere (displayln INDEX)))
0
1
2
3
4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I need to do something extra, since the use of interfere adds an
additional syntax parameterization that breaks the lexicalness I
expected from INDEX.
I can do something that appears to do the trick, but I'm a bit
uncomfortable with it:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang racket
(require racket/stxparam)
(begin-for-syntax
(define internal-index-id (gensym 'internal-index-id)))
(define-syntax (ranged stx)
(syntax-case stx ()
[(_ n body ...)
(with-syntax ([k (datum->syntax stx internal-index-id)])
(syntax/loc stx
(for ([k n])
(begin body ...))))]))
(define-syntax (INDEX stx)
(datum->syntax stx internal-index-id))
(define-syntax (interfere stx)
(syntax-case stx ()
[(_ body ...)
(syntax/loc stx
(ranged 1 body ...))]))
(ranged 5 (interfere (displayln INDEX)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
The use of the gensym there is what makes me uncomfortable. I'm not
exactly sure what the right approach is supposed to be here, though.
Suggestions?
Thank you!