読者です 読者をやめる 読者になる 読者になる

Syntactic closure

Scheme の話題がネタ切れ気味になってきたので、過去に書いたものを見返していたところ、 R6RS で explicit renaming を実装するというネタが目に止まった。

http://saito.hatenablog.jp/entry/20110914/1316020026

そういえば syntactic closure もいずれやってみようと思っていたのだった。

で、出来上がったのがこちら。

#!r6rs
(library (syntactic-closure)
  (export sc-macro-transformer make-syntactic-closure)
  (import (rnrs))

  (define (tree-walk proc ls)
    (let loop ((ls ls))
      (cond ((null? ls) '())
            ((pair? ls) (cons (loop (car ls)) (loop (cdr ls))))
            (else (proc ls)))))

  (define-syntax sc-macro-transformer
    (lambda(stx2)
      (syntax-case stx2 ()
        ((k proc)
         #'(lambda(stx)
             (syntax-case stx ()
               ((env . arg)
                (tree-walk
                 (lambda(x) (if (symbol? x) (datum->syntax #'k x) x))
                 (proc (syntax->datum #'(env . arg)) #'env)))))))))

  (define (make-syntactic-closure env names form)
    (tree-walk 
     (lambda(x)
       (if (symbol? x)
           (if (memv x names) x (datum->syntax env x))
           x))
     form))
  )

使用例はこんな感じ。

#!r6rs
(import (rnrs)
        (for (syntactic-closure) expand))

(define-syntax swap! 
  (sc-macro-transformer 
   (lambda (form env) 
     (let ((a (make-syntactic-closure env '() (cadr form))) 
           (b (make-syntactic-closure env '() (caddr form)))) 
       `(let ((value ,a)) 
          (set! ,a ,b)
          (set! ,b value))))))

(define c 1)
(define d 2)

(swap! c d)

(display c) (newline)
(display d) (newline)

いくつかの処理系で試していたのだが、 Ypsilon でだけ以下のような例がエラーになってしまった。 let-syntax の定義部での文脈をうまく扱えないらしい。

#!r6rs
(import (rnrs)
        (for (syntactic-closure) expand))

(let-syntax ((foo (sc-macro-transformer
                   (lambda(expr env)
                     (let ((var (cadr expr)))
                       `(display ,(make-syntactic-closure env '() var)))))))
  (let ((x 2))
    (foo x)))

Document ID: f4f2d89748919445e1bcb89d09350dd8