前回は Scheme (R6RS) のマクロを使って名前空間的なものを実現しようと試みた。
id:SaitoAtsushi:20100828:1282960407
それについて id:leque 氏より興味深いコメントを頂いた。
これは datum->syntax を使わなくても書けるように思います。
(define-syntax make-namespace (lambda (x) (syntax-case x (define) ((k name (define var val) ...) (with-syntax (((tmp ...) (generate-temporaries #'(var ...)))) #'(begin (define tmp val) ... (define-syntax name (syntax-rules () ((_ n) (letrec-syntax ((find (syntax-rules (n) ((_ (n . _) (v . _)) v) ((_ (_ . ns) (_ . vs)) (find ns vs))))) (find (var ...) (tmp ...))))))))))))変数名を作るのに generate-temporaries を使っていますが、ここも syntax-rules で書けば syntax-rules だけで書けます。
http://d.hatena.ne.jp/SaitoAtsushi/20100828/1282960407#c1282976093
このコメントを受けて試しに syntax-case や generate-temporaries を使わないように書き直してみた。
(define-syntax %make-namespace (syntax-rules (define) ((_ name (binds ...) (define (var args ...) val) next ...) (begin (define (tmp args ...) val) (%make-namespace name ((var tmp) binds ...) next ...))) ((_ name (binds ...) (define var val) next ...) (begin (define tmp val) (%make-namespace name ((var tmp) binds ...) next ...))) ((_ name (binds ...) exp next ...) (begin exp (%make-namespace name (binds ...) next ...))) ((_ name (binds ...)) (define-syntax name (syntax-rules () ((_ id) (letrec-syntax ((find (syntax-rules (id) ((_ (id v) next ((... ...) (... ...))) v) ((_ p next ((... ...) (... ...))) (find next ((... ...) (... ...))))))) (find binds ...)))))))) (define-syntax make-namespace (syntax-rules () ((_ name exp next ...) (%make-namespace name () exp next ...))))
MIT 記法も OK なのと、定義以外の式も書けるようにした。
が、ここで致命的な欠点が発覚。 make-namespace の内部では御互いに変数へアクセスできない。
(make-namespace ns (define a 1) (define b (+ a 1)) ;; ←これ駄目 )
これではとても実用レベルにはなりそうもない。
ただ、アクセス不能な変数とそれに対するインターフェイスとしてのマクロというパターンは何かと使えそうな気がする。 その意味では先日のネタ (id:SaitoAtsushi:20100822:1282449327) と共通のパターンになっている。
Document ID: 3064c1bfec3f358f4a81cf8ab1669866