LISP 系言語の強さの源泉のひとつがマクロにあることは疑いない。 だが、このマクロにはいくつもの方式がある。 Scheme の最新規格であるところの R6RS では syntax-case が採用されたが、それが最適な選択だと誰もが認めているわけではない。
意見が割れる理由は明白だ。
syntax-rulesは不自由すぎるのだけれど、それ以外の衛生的マクロ、syntax-caseや explicit renaming 等はどれも力の上では同じであることがわかっていて、ひとつがあれば別のやつはポータブルに実装できることがわかっている。
これは興味深い話だと思う。
マクロを使って別のマクロの方式を実装するというのをやってみようと思ったが、私は R5RS や R6RS 以外にあまり注目したことがなかったので、具体的にどんな方式があるのかと検索してみるととてもわかりやすい解説を見付けた。
比較的簡単そうに思えた 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 ではどうなることか。
Document ID: 62aff67246b8a0ed93a20529ad56eef3
0 件のコメント:
コメントを投稿