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

続々・名前空間

先日から Scheme (R6RS) のマクロを使って名前空間的なものを実現しようと取り組んでいる。
id:SaitoAtsushi:20100829:1283037554
が、 make-namespace の内部で御互いの変数へアクセスできないという致命的な欠陥が発覚したのであった。

(make-namespace ns
  (define a 1)
  (define b (+ a 1)) ;; ←これ駄目
  )

と言うのがここまでのあらすじ。
で、今回も id:leque 氏より提案を頂いた。

なるほど、定義時におたがいが見えないとまずいですね。うーん……
↓のような感じで、 let () で作ったローカルな環境内で define してあとから外の変数に set! するのはどうでしょう。

#'(begin
    (define tmp) ...
    (define-syntax name
      ;; 略
      )
    (define dummy
      (let ()
        (define var val) ...
        (set! tmp var) ...
        0)))

make-namespace をライブラリトップレベルで並べても叱られないように最後の式も define で囲んでみました。

http://d.hatena.ne.jp/SaitoAtsushi/20100829/1283037554#c1283045902

これは良い案だと思うのだけれど、 マクロ展開時に解決という縛りを最初にしてしまったので、実行時に set! される方式はあえて避けて別の方法を考えてみた。

(define-syntax %make-namespace
  (syntax-rules (define)
    ((_ name (binds ...) (exps ...) (define (var args ...) body ...) next ...)
     (%make-namespace name
                      ((var tmp) binds ...)
                      (exps ... (define (tmp args ...) body ...))
                      next ...))
    ((_ name (binds ...) (exps ...) (define var val) next ...)
     (%make-namespace name
                      ((var tmp)  binds ...)
                      (exps ... (define tmp val))
                      next ...))
    ((_ name (binds ...) (exps ...) exp next ...)
     (%make-namespace name (binds ...) (exps ... exp) next ...))
    ((_ name ((var tmp) ...) (exps ...))
     (begin
       (let-syntax ((var
                     (make-variable-transformer
                      (lambda(x)
                        (syntax-case x (set!)
                          ((set! _ val) #'(set! tmp val))
                          (_ #'tmp))))) ...)
         exps ...)
       (define-syntax name
         (syntax-rules ()
           ((_ id)
            (letrec-syntax ((find
                             (syntax-rules (id)
                               ((_ (id v) next ((... ...)(... ...)))
                                v)
                               ((_  p next ((... ...)(... ...)))
                                (find next ((... ...)(... ...))))
                               )))
              (find (var tmp) ...)))))
       ))))

(define-syntax make-namespace
  (syntax-rules ()
      ((_ name exp next ...)
       (%make-namespace name () () exp next ...))))

ついでに make-namespace 内で定義された変数に make-namespace 内で set! することが出来るようにした。 make-namespace 内で定義された変数に make-namespace 外で set! することは出来ないのだけれど、それはライブラリでもそうだからまあよかろうということで。

(make-namespace ns
  (define a 1)
  (define b (+ a 1)) ;; ←これが出来るようになった
  (set! a 2) ;; ←ついでにこれも出来るようになった
  )

(display (ns a)) ;; ←これで 2 が表示されるはず
(set! (ns a) 3) ;; ←こういうのは出来ない

ところで今回のマクロを Ypsilon の R6RS モード (オプションに -6 を付ける) で動かすとエラーになってしまうのだけれど、何か変なことしているだろうか。
Document ID: 5b96a361b889af1fb437415db432d1e2