環境を汚さないrequire

トップレベルで作業をする時に、どうしてもライブラリを利用したくなりますが、その時にrequireで幾つもロードしていると、何となくREPLを汚してしまってるような気分になります。

実際それで困ることはまず無いんですが、たまに基本構文のセマンティクスが変わって戸惑うこともあったりします。

そこで思い出したのが名前空間です。

PLT Schemeでは名前空間が第一級オブジェクトなので、式評価の環境を自在に作ったり変更したりできるんです。

例えば

> (current-namespace (make-empty-namespace))

とするとREPLの環境がまっさらな状態になる、という具合です (関数適用すらできなくなります)。

これを利用して、ローカルな環境の中だけでライブラリを読み込む構文を作ってみました。

(begin-for-syntax
  (define req-specs
    (append '(for-meta for-syntax for-template for-label just-meta)
            '(only prefix all-except prefix-all-except rename)))

  (define (req-spec? spec)
    (or (module-path? spec)
        (and (pair? spec)
             (memq (car spec) req-specs)))))

(define-syntax (with-require stx)
  (syntax-case stx ()
    ((with-require mod . body)
     (req-spec? (syntax->datum #'mod))
     #'(with-require (mod) . body))
    ((with-require (mod ...) . body)
     (andmap req-spec? (syntax->datum #'(mod ...)))
     #'(parameterize ((current-namespace (make-base-namespace)))
         (for-each namespace-require '(mod ...))
         (eval '(begin . body))))))

> (with-require "prelude.ss"
    (aif 0 (add1 it)))
1
> aif
reference to undefined identifier: aif


参考:
http://docs.plt-scheme.org/guide/eval.html
http://docs.plt-scheme.org/guide/mk-namespace.html