For Development HEAD DRAFTSearch (procedure/syntax/module):

10.1 R7RS統合


10.1.1 二つの世界を往き来する

Gaucheを起動した直後は、それがREPLであってもスクリプトファイルの実行であっても、 トップレベルはuserモジュールになっています。userモジュールは gaucheモジュールを継承しています。 また、ライブラリを読み込む際にも、(select-moduleが呼ばれるまでは) トップレベルはgaucheを継承したモジュールになっています。 これが、(use something)等といちいち書かないでも Gaucheの組み込み手続きを使える理由です。 (モジュールの継承についてはモジュールの継承を参照してください)。

一方、R7RSではimportフォームによってどの名前空間を利用するか明示することが 求められています(例: (import (scheme base)))。 また、R7RSライブラリはそれぞれdefine-libraryフォームで囲う必要があります。 プログラムの最初のimportより前、あるいはライブラリのdefine-libraryの 外側は、R7RSの世界の外であり、標準では何も規定していません。

Gaucheはこれを利用して、適切な「世界」を設定し、R7RSコードと既存のGaucheコードを 透過的に混ぜられるようにしています。

註:3つのimport形式で説明しますが、 R7RSのimportはGaucheのimportと異なります。この節では 区別が必要な時は前者をr7rs#import、後者をgauche#importと 表記しますが、実際のコードではr7rs#gauche#といったプレフィクスを つけずに、単にimportと書いてください。

R7RSライブラリをロードする

define-libraryフォーム自身は、gaucheモジュール内のマクロとして 定義されていて、R7RS環境を設定してその中身を評価するコードに展開されます。 (Gaucheコードからuseを使うか、 R7RSコードからr7rs#importフォームを使うかして)R7RSライブラリをロードすると、 Gaucheはファイルの評価をgaucheモジュール内で始めますが、すぐに define-libraryフォームに当たるので、その中身はR7RS環境で処理されることになります。

以下のコードを持つ、R7RSライブラリ(mylib foo)があるとしましょう。

(define-library (mylib foo)
  (import (scheme base))
  (export snoc)
  (begin
    (define (snoc x y) (cons y x))))

このファイルは*load-path*にあるディレクトリのどれかの下に、 mylib/foo.scmという名前で保存されなければなりません。

R7RSコードからは、このライブラリはr7rs#importフォームでロードできます。

(import (mylib foo))

(snoc 1 2) ⇒ (2 . 1)

Gaucheコードからこのライブラリを使うには、ライブラリ名の各要素を.で つなげたものをモジュール名として、useします。

(use mylib.foo)

(snoc 1 2) ⇒ (2 . 1)

Gaucheライブラリのロード

Gaucheライブラリのfoo.barをR7RSコードから使うには、 モジュール名を.で区切った名前のリストをライブラリ名として使います。 例えばgauche.lazyモジュールは次のとおりR7RSから使えます。

(import (gauche lazy))

SRFIモジュールについては、R7RSでは慣習的に(srfi n)という 名前を使うので、Gaucheもそれに合わせています。R7RSコードから SRFI-1とSRFI-13を使うには、次のようにします。

(import (srfi 1) (srfi 13))

(これが動くのは、Gaucheがsrfiという名前を特別扱いしているのではなく、 Gaucheをインストールした時にsrfi/1.scm等のアダプタライブラリが作られているからです)

ヒント: Gaucheの組み込みの機能 (Gaucheコードでは最初から使える束縛) を R7RSコードから使いたい場合は、(gauche base)ライブラリをimportしてください (gauche.base - Gauche組み込み関数のインポート参照)。

(import (gauche base))

filter ⇒ #<closure filter>

R7RSスクリプトの実行

R7RSスクリプトは必ずimportフォームで始まります。 しかし、r7rs#importgauche#importと文法や意味が異なります。 そこで、ちょっとしたトリックを使っています。

goshがスクリプトファイルを読み込むのはuserモジュールです。 そこにはuser#importというマクロが用意してあり、 そのマクロは引数を調べて、それがR7RSのimportの構文であれば r7rs.userモジュールに切り替えてr7rs#importを実行し、 そうでなければgauche#importを実行します。 詳しくは3つのimport形式を参照してください。

R7RSスクリプトの例です:

(import (scheme base) (scheme write))
(display "Hello, world!\n")

Gaucheスクリプトを見慣れている人は、R7RSプログラムがmain手続きを 特別扱いしないことに気をつけてください。R7RSプログラムは、トップレベルフォームを 順に評価してゆくだけです。したがって次のスクリプトは何も表示しません:

(import (scheme base) (scheme write))
(define (main args)
  (display "Hello, world!\n")
  0)

R7RSスクリプトからコマンドライン引数を利用するには、 (scheme process-context)ライブラリのcommand-lineを使います (scheme.process-context - R7RSプロセスコンテキスト および コマンドライン引数参照)。

R7RS REPLを使う

gosh-r7オプションつきで起動され、スクリプトファイルが 与えられていなかった場合は、R7RS REPLモードになります。 使いやすいように、以下のモジュール (R7RS用語では「ライブラリ」)は 自動的にロードされます。

(scheme base) (scheme case-lambda) (scheme char)
(scheme complex) (scheme cxr) (scheme eval)
(scheme file) (scheme inexact) (scheme lazy)
(scheme load) (scheme process-context) (scheme read)
(scheme repl) (scheme time) (scheme write)

さらに、ヒストリ変数*1*2*3*1+*2+*3+*e および *historyも使えます (ヒストリ変数の詳細についてはREPLでの開発参照)。

R7RS REPLにいることはプロンプトを見ればわかります。 現在のモジュールがr7rs.userとなっているからです。

gosh[r7rs.user]>

R7RS REPLからGauche REPLにスイッチするには、(gauche base)を インポートしてselect-moduleを使ってuserモジュールを選びます。

gosh[r7rs.user]> (import (gauche base))
#<undef>
gosh[r7rs.user]> (select-module user)
#<undef>
gosh>

((select-module gauche)とする手もありますが、おすすめはしません。 gaucheモジュールの中を変えると思いがけない副作用が生じるかもしれませんから。)

R7RSコードをファイルに書きだし、R7RS REPLにそのファイルをロードしてインタラクティブに 開発を進めている場合 (例えばEmacsのSchemeモードなら、C-c C-lでファイルがロードできます)、 そのファイルがR7RSとして正しい形式になっていることを確認してください。 つまり、ファイルはimport宣言で始まるか、define-library形式 から構成されるものでなければなりません。それ以外の、単にSchemeコードが並べて 書いてあるだけのファイルをロードした場合、 それはREPLでの現在のモジュールが何であるかにかかわらずGaucheのuserモジュールに 読み込まれるので、r7rs.userモジュールからはデフォルトでは見えません。

Gauche REPLからのスイッチ

デフォルトでは、goshはスクリプトファイルが与えられなければGaucheのREPLに 入ります。REPLの説明はREPLでの開発を参照してください。

Gauche REPLからR7RS REPLにスイッチするには、 単にr7rsスタイルのimportを使うだけです。user#importは あなたが欲しいのがR7RSであることを察知して環境を切り替えます。

gosh> (import (scheme base))
#<undef>
gosh[r7rs.user]>

ただし、gosh-r7オプションつきで起動していなければ、 この時点でロードされているのはuser#importで指定したライブラリだけです。

もし、「すっぴん」のR7RS環境、つまり(scheme base)でさえロードされていない 環境へと切り替えたければ、r7rsモジュールをuseして 直接r7rs.userモジュールを選択します。

gosh> (use r7rs)
gosh> (select-module r7rs.user)
gosh[r7rs.user]>

こうして切り替えた場合、使えるフォームはimportdefine-library だけです (defineでさえ定義されていません!) この環境でSchemeを書くには、まず (import (scheme base)) 等として 束縛をインポートする必要があります。


10.1.2 3つのimport形式

歴史的な事情から、Gaucheにはimport形式が3つあります。 もともとのGaucheのimport、R7RSのimport、 そしてハイブリッドのimportです。

通常、コードを読み書きしている時は、それが伝統的なGaucheコードかR7RSコードかは 明確ですし、またimportの使われ方はだいたい決まっているので、 現場で混乱が起きることはありません。ただ、コードの外でimportについて 話す場合は、どのimportのことかをはっきりさせるのが良いでしょう。

ハイブリッドのimportは前節で説明したuser#importです (二つの世界を往き来する参照)。 user#importは GaucheのimportとR7RSのimportの両方の文法を理解できるので、 実際に知る必要のあるのは最初の2つの形式です。

Gaucheのモジュールシステム設計はSTkを継承していて、 importは純粋に名前空間の操作として使われています。 つまり、既にメモリ上に存在するモジュールをインポートするということです。 そのモジュールが定義されているファイルを、(必要なら)ロードするという 操作は別のプリミティブrequireにより実現されます。 典型的には、ひとつのファイルがひとつのモジュールを定義していて、 そのモジュールを使うにはまずファイルをrequireし、 次にモジュールをimportするということになります (これはGaucheでは頻繁に出てくるので、useというマクロが定義されているくらいです)。 ファイルのロードとモジュールのインポートが分かれていることは、 たまに変わったハックが必要とされる時に役に立ちます。 Gaucheのimportについてはモジュールの使用を参照してください。

R7RSは実装に選択の余地を与えるため、モジュール(ライブラリ)とファイルの関係は 敢えて規定していません。R7RSのimportは、 必要ならば暗黙のうちに、透過的にファイルをロードすることが期待されています。 つまり、R7RSのimportはGaucheのuseと意味的に同じと考えられます。

ハイブリッドimportはSchemeスクリプトの最初にのみ現れて、 実行されるスクリプトが伝統的なGaucheコードであるかR7RSコードであるかを 判断します。詳細は二つの世界を往き来するを参照してください。

ではR7RSのimportを説明します:

Special Form: import import-spec …

[R7RS] import-specで指定されるライブラリをインポートします。 R7RSでいうライブラリは、Gaucheがモジュールと呼んできたものです。両者は同じものです。

R7RSライブラリの名前はシンボルか整数のリストで表されます。 例: (scheme base)(srfi 1)。 この名前は、ピリオドを挟んでシンボル名をつなげることで、Gaucheのモジュール名へと 変換されます。つまり、R7RSの(scheme base)はGaucheからはscheme.base モジュールとして扱えます。逆にGaucheのdata.queueモジュールは R7RSからは(data queue)ライブラリとして扱えます。 R7RSプログラムからこれら二つのライブラリを使うには、プログラムの最初に 次のように記します。

(import (scheme base)
        (data queue))

これはGaucheでuseフォームを使うのと同じです。つまり、もし その名前を持つモジュールが現在のプロセス内に無ければまずファイルをロードし、 それからモジュールがexportしている束縛を現在のモジュールから見えるようにします。

(use scheme.base)
(use data.queue)

(勘の良い読者は、R7RSのライブラリがピリオドを中に含むシンボルを名前に使っていたら どうなるのか疑問に思ったことでしょう。正直なところ、まだどうするか決めていません。 多分、何らかのエスケープ機構を用意することになると思いますが、 今のところ、できる限りモジュール名にはアルファベット、数字、ハイフンだけを 使うようにしておいて下さい。)

R7RSのimportフォームは、 Gaucheのuseと同様、どのシンボルをimportするか/しないかを指定したり、 名前を変えてimportしたりプレフィクスを付加することができます。 R7RSのimportの正式な構文は次の通りです。

<import declaration> : (import <import-set> <import-set> ...)

<import-set> : <library-name>
  | (only <import-set> <identifier> <identifier> ...)
  | (except <import-set> <identifier> <identifier> ...)
  | (prefix <import-set> <identifier>)
  | (rename <import-set>
            (<identifier> <identifier>)
            (<identifier> <identifier>) ...)

<library-name> : (<identifier-or-base-10-integer>
                  <identifier-or-base-10-integer> ...)


For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT