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

名前空間

Scheme (R6RS) ではライブラリが名前空間として機能する。 だが、それとは別に単一のプログラム、あるいはライブラリの内部を名前空間で区切ることが出来ないものかと考えた。 そこで以下のようなマクロが出来上がった。

(define-syntax make-namespace
  (lambda(x)

    (define binds '())

    (define (generate-internal-name x)
      (car (generate-temporaries (list x))))

    (define (var-name lst)
      (syntax-case lst (define)
        ((define var val)
         (with-syntax ((t (generate-internal-name #'var)))
           (set! binds (cons (cons #'var #'t) binds))
           #'(define t val)))
        ((define (var arg ...) val)
         (with-syntax ((t (generate-internal-name #'var)))
           (set! binds (cons (cons #'var #'t) binds))
           #'(define (t arg ...) val)))
        (a #'a)))

    (define (make-switch)
      (map (lambda(x) #`((free-identifier=? #'#,(car x) #'var) #'#,(cdr x)))
           binds))
    
    (syntax-case x ()
      ((k name body0 body1 ...)
       (with-syntax (((defs ...) (map var-name #'(body0 body1 ...))))
         (with-syntax ((binds (datum->syntax #'k binds)))
         #`(begin
             defs ...
             (define-syntax name
               (lambda(x)
                 (syntax-case x ()
                   ((_ var)
                    (identifier? #'var)
                    (cond #,@(make-switch)
                          (else #'#f))
                    )))))))))
    ))

別の名前空間に属する変数は同じ名前であっても別物として扱われる。

(make-namespace ns1
  (define a 1)
  (define b 2)
  )

(make-namespace ns2
  (define a 'a) ;; ns1 の a とは別物として扱われる
  (define b 'b)
 )

(display (ns1 a)) (newline)
(display (ns1 b)) (newline)
(display (ns2 a)) (newline)
(display (ns2 b)) (newline)

マクロ展開時に参照は解決されるので、性能に影響することもない。
ところで、当初書いていたマクロはうまく機能せず、にちゃんねるで質問してみた。
http://hibari.2ch.net/test/read.cgi/tech/1270897776/923
イマイチ妥当な解答は得られなかったのだが、別のコンセプトの提案があった。
http://hibari.2ch.net/test/read.cgi/tech/1270897776/935
今回はこの提案を踏襲している。
R6RS のマクロで一番難しいのは datum->syntax だと思っている。 未だに理解できた気がしない。
Document ID: 7cdb745d81f9fd4e229cd3ceefa47fc4