• 二つの世界を往き来する: | ||
• 3つのimport形式: |
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
と書いてください。
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ライブラリの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スクリプトは必ずimport
フォームで始まります。
しかし、r7rs#import
はgauche#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プロセスコンテキスト および コマンドライン引数参照)。
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
モジュールからはデフォルトでは見えません。
デフォルトでは、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]>
こうして切り替えた場合、使えるフォームはimport
とdefine-library
だけです (define
でさえ定義されていません!)
この環境でSchemeを書くには、まず (import (scheme base))
等として
束縛をインポートする必要があります。
歴史的な事情から、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
を説明します:
[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> ...)