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

explicit-renaming

LISP 系言語の強さの源泉のひとつがマクロにあることは疑いない。 だが、このマクロにはいくつもの方式がある。 Scheme の最新規格であるところの R6RS では syntax-case が採用されたが、それが最適な選択だと誰もが認めているわけではない。
意見が割れる理由は明白だ。

syntax-rules は不自由すぎるのだけれど、それ以外の衛生的マクロ、 syntax-case や explicit renaming 等はどれも力の上では同じであることがわかっていて、ひとつがあれば別のやつはポータブルに実装できることがわかっている。

http://blog.practical-scheme.net/shiro/20100425-scheme-macro

これは興味深い話だと思う。
マクロを使って別のマクロの方式を実装するというのをやってみようと思ったが、私は R5RS や R6RS 以外にあまり注目したことがなかったので、具体的にどんな方式があるのかと検索してみるととてもわかりやすい解説を見付けた。
http://d.hatena.ne.jp/leque/20080528/p1
比較的簡単そうに思えた Explicit Renaming (er-macro-transformer) を syntax-case で書いてみたのが以下だ。

#!r6rs
(library (explicit-renaming)
  (export er-macro-transformer)
  (import (rnrs))

  (define-syntax er-macro-transformer
    (lambda(x)
      (syntax-case x ()
        ((k exp)
         (with-syntax ((identifier? (datum->syntax #'k 'identifier?)))
           #'(lambda(stx)
               
               (define (walk proc ls)
                 (let loop ((ls ls))
                   (cond ((null? ls) '())
                         ((pair? ls) (cons (loop (car ls)) (loop (cdr ls))))
                         (else (proc ls)))))
               
               (define (rename sym) (datum->syntax #'k sym))
               
               (syntax-case stx ()
                 ((t a (... ...))
                  (let* ((symbol->identifier
                          (lambda(x)
                            (if (symbol? x) (datum->syntax #'t x) x)))
                         (compare 
                          (lambda(e1 e2)
                            (let ((e1 (symbol->identifier e1))
                                  (e2 (symbol->identifier e2)))
                              (if (and (identifier? e1)
                                       (identifier? e2))
                                  (free-identifier=? e1 e2)
                                  #f)))))
                    (walk
                     symbol->identifier
                     ((let ((identifier?
                             (lambda(e)(or (identifier? e) (symbol? e)))))
                        exp)
                      (syntax->datum #'(t a (... ...)))
                      rename
                      compare)))))))))))
  )

使用例も書いておく。

#!r6rs
(import (rnrs)
        (for (explicit-renaming) expand))

(define-syntax swap! 
  (er-macro-transformer
   (lambda (form rename compare) 
     (let ((a (cadr form))
           (b (caddr form)))
       `(,(rename 'let) ((,(rename 'value) ,a))
         (,(rename 'set!) ,a ,b)
         (,(rename 'set!) ,b ,(rename 'value)))))))

(define c 1)
(define d 2)

(swap! c d)

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

割と簡単に書けた。 Syntactic Closure を書くのもそれほど難しくはないのではないかと思う。
逆に Explicit Renaming や Syntactic Closure で syntax-case を書くのはとても面倒なものになるだろう。 そういう意味でユーザーとしての立場で見れば syntax-case はとても良いと思うのだが、機能を詰め込みすぎて Scheme の風潮に合わないというのもよくわかる。
次期規格である R7RS ではどうなることか。 Scheme の規格は改訂のたびに根本を変えすぎだよなぁ。
Document ID: 62aff67246b8a0ed93a20529ad56eef3