[racket] Another macro question: making a lexically binding keyword?

From: Danny Yoo (dyoo at cs.wpi.edu)
Date: Sun Apr 29 15:54:35 EDT 2012

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!

Posted on the users mailing list.