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

6.22 プログラムのロード


6.22.1 Schemeファイルのロード

Function: load file :key paths (error-if-not-found #t) environment ignore-coding

[R7RS+ load] fileをロードします。すなわち、fileに書かれたScheme式を順次読み込んで 評価します。fileの拡張子 (“.scm”) は省略できます。

fileが “/”, “./” または “../” で始まっていない場合は、 システムファイルサーチパス (変数*load-path* に格納されています) の中から該当ファイルが探されます。あるいは、キーワード引数pathsにディレクトリ名の リストを渡すことによりサーチパスを指定することもできます。

ロードに成功した場合は、実際にロードされたファイルのパス名が文字列で返されます。 指定ファイルがみつからない場合はエラーとなりますが、もしキーワード引数 error-if-not-found#fが与えられていれば単にloadから #fが返されます。

デフォルトでは、loadはコーディング認識ポート(コーディング認識ポート参照)を 使ってソースファイルを読み込むので、ファイル先頭の"coding:" コメントによる 文字エンコーディング指定が有効になります。 (codingコメントについてはマルチバイトスクリプトを参照)。 但し、キーワード引数ignore-codingに真の値が渡された場合、 loadはcoding-aware portを作成せず、直接ファイルポートから ソースを読み込みます。

キーワード引数environmentにモジュールが渡された場合は、 あたかもそのモジュールがファイルの先頭でselectされたかのように loadされます。

カレントモジュールは保存されます。つまり、file中でselect-moduleによって カレントモジュールを変更しても、loadが終わったらloadを読んだ時点の モジュールに戻ります。

GaucheのloadはR5RSのloadの上位互換ですが、 R7RSのloadは省略可能引数が異なります。scheme.load - R7RS load参照。

ライブラリファイルをロードする場合は、‘use’ (モジュールの定義と選択参照) を 使うか、下に説明する‘require’ を使う方が良いでしょう。 loadrequireの違いについてはコンパイルを参照してください。

Variable: *load-path*

loadrequireがファイルを探すディレクトリのリストを保持しています。

もしサーチパスにディレクトリを追加したい場合は、この変数を直接変更せずに、下に説明する add-load-pathを用いて下さい。

Special Form: add-load-path path flag …

パスpathをライブラリロードパスのリストに加えます。 ロードパスはコンパイル時に確定している必要があるので、 pathはリテラル文字列でなければなりません。 pathが相対パスの場合、:relativeフラグが与えられて いなければ、プロセスの現在のワーキングディレクトリからの相対と解釈されます。

pathは存在する必要はありません。ロードパスに存在しないディレクトリが含まれていても 単に無視されるだけです。ただ、pathが存在する場合、add-load-pathは アーキテクチャ依存パスも探します。後で詳しく説明します。

以下の値がflag引数として認識されます。

:after

pathを現在のロードパスリストの末尾に加えます。 デフォルトでは、pathはロードパスリストの先頭に加えられます。

#t

:afterと同じです。互換性のために認識されます。

:relative

pathを、(カレントワーキングディレクトリではなく) 現在ロード中のファイルのあるディレクトリからの相対パスとして 解釈します。現在ロード中のファイルが不明な場合 (REPLから 評価されたり、ソケットから読まれたりしている場合) は、このフラグは無視されます。

ロードパスを変更したい場合、*load-path*を直接替えずにこのフォームを 使って下さい。このフォームはコンパイル時に解釈されるのに対し、*load-path*を 書き換えるコードは実行時に解釈されます。“use” や “require” は コンパイル時のロードパスを使うので、*load-path*への変更は反映されないかもしれません。

更に、add-load-pathpathの下にアーキテクチャ依存のディレクトリが ないかどうかを探し、あればそれを内部の共有ライブラリサーチパスに追加します。 例えばあなたが自分のSchemeモジュールを/home/yours/libに入れていて、 それが共有ライブラリを必要としていたとします。手続きgauche-architecture (環境の問い合わせ参照)が返す値をARCHとして、 共有ライブラリを/home/yours/lib/ARCH/に置いておくと、 共有ライブラリはそこからロードされます。この方法を取ると、複数のプラットフォーム用に 別々にコンパイルされた共有ライブラリを管理することができます。

Function: load-from-port port

入力ポートportから、EOFを読むまで繰り返しScheme式を読み込み評価します。

portにコーディング認識ポートを渡さない限り、"coding:"コメント による文字コード変換は行われないことに注意して下さい。

Function: current-load-port
Function: current-load-path
Function: current-load-history
Function: current-load-next

これらの手続きによって、現在のロードのコンテクストを知ることができます。 ロードされているファイルの中でこれらの手続きを呼ぶと、次のような値が返されます。

current-load-port

現在のフォームがロードされている入力ポート。

current-load-path

現在のフォームがロードされているファイル。 ロードのソースがファイルでない場合、この値は#f

current-load-history

入力ポートと行番号のペアのリストで、ロードのネスティングを示したもの。 例えばあなたがfoo.scmをロードし、そのファイルの7行目で bar.scmがロードされ、そのファイルの18行目でbaz.scmが ロードされたとします。current-load-historybaz.scm中で 呼ぶと、それは次のような値を返します。

((#<port "foo.scm"> . 7) (#<port "bar.scm"> . 18))
current-load-next

現在のファイルがロードされた時点での、ファイルサーチパスの残りを返します。 例えば *load-path*("." "../lib" "/home/gauche/lib" "/share/gauche/lib")で、 あなたがfoo.scmをロードしたところ、それが../lib/中に見つかったと しましょう。このとき、foo.scm中でcurrent-load-nextを呼べば

("/home/gauche/lib" "/share/gauche/lib")

が返されます。

loadされていない状態で呼ばれた時は、これらの手続きはそれぞれ#f#f()()を返します。


6.22.2 ダイナミックライブラリのロード

Function: dynamic-load file :key init-function

ダイナミックローダブルライブラリ(共有ライブラリ)fileをロードしてリンクします。 fileにはサフィックス(“.so” 等) を含めないで下さい。システムによって サフィックスは異なるため、dynamic-loadがそれを追加します。

キーワード引数init-functionは共有ライブラリ中の初期化関数の名前を 指定します。デフォルトでは、サフィックスを除くファイル名が “foo” の場合、 初期化関数名は “Scm_Init_foo” となります。

通常、共有ライブラリはSchemeモジュール中でロードされるので、モジュールユーザが 直接この手続きを呼ぶ必要はほとんどないでしょう。

一度ロードされた共有ライブラリをアンロードすることはできません。


6.22.3 requireとprovide

requireprovideは、Lispでライブラリファイルを一度だけ 読み込むことを保証するための伝統的な方法です。 あるfeatureを最初にrequireすると、その機能を提供する ライブラリファイルがロードされ、その機能が提供されたということが記憶されます。 2回め以降のそのfeatureのリクエストではファイルはロードされません。

Gaucheではuse構文 (モジュールの使用参照) が requireのメカニズムをボンネットの下に隠してくれるので、 これらの式を直接目にする必要はほとんどありません。 もしあなたがちょっと変わった方式でライブラリを構成したくて、したがって Gaucheの標準的なメカニズムをバイパスしたい場合に限り、使うようにしてください。

Special Form: require feature

featureがまだロードされていなければロードします。featureは文字列で なければなりません。それがそのまま(サフィックスを除く)ファイル名としてロードパスから 探されます。requireの解釈はコンパイル時に行われます。

SLIBモジュールをロードすると、requireが拡張されます。詳しくはslib - SLIBインタフェースを 参照して下さい。

もしロードされたファイルがprovideフォームを含んでいなかった場合、 あたかもファイルの最後に(provide feature)があったかのように、 featureが自動的にprovideされます。これを autoprovide機能と呼んでいます。

requireは、ファイルをロードする前に現在のモジュールを gauche.require-baseという変更不可なモジュールにセットします。 requireされるファイルは通常、最初にdefine-module/select-moduledefine-libraryフォームを持つので、このgauche.require-baseモジュール を目にすることはほとんど無いでしょう。 ただ、もしロードされたファイルがモジュールを指定すること無くトップレベルの 変数を定義したり他のモジュールをimport(use)しようとした場合、 次のようなエラーとなります。

*** ERROR: Attempted to create a binding (a) in a sealed
module: #<module gauche.require-base>

理由: requireされているファイルがどのタイミングで読まれるかを 正確にコントロールするのは難しいです(他のモジュールが既にrequireしているかも しれないので)。もし呼び出し側の現在のモジュールをそのまま使った場合、二つの 問題が生じ得ます。(1)呼び出し側の現在のモジュールから、 define-moduledefine-libraryが見えているとは限りません。 (2)モジュールを指定しないトップレベル定義が呼び出し側の現在のモジュールに 定義を追加することは保証されません(既に別のモジュールへと読みこまれているかもしれません)。 モジュールを指定しないトップレベル定義やimportを持つファイルをrequireすることは、 単に悪いアイディアです。したがってそういう事例はエラーとすることにしました。

Function: provide feature

featureをシステムのprovideされたフィーチャーリストに加えます。 以降、featurerequireされてもファイルはふたたびロードされません。

requireが要求された機能を自動的にprovideされたかのように 扱うAutoprovide機能があるために、provideを明示的に呼ぶ必要は ほとんど無いでしょう。provideを使いたいシチュエーションとしては 次のようなものが考えられます。

  • そのファイルをロードすることになったfeatureとは異なるfeature (複数可) を provideしたい場合。

    例えばfeature Xがfeature Yを置き換えるものだとします。 XはYのAPIを全て提供しますが、実装は異なります。この場合、 X.scmが一度ロードされたら、Y.scmをロードしてほしくないと思うでしょう。 X.scm(provide "X")(provide "Y") の両方を 書いておくことで、X.scmがfeature Yもprovideすると明示すれば、それが実現できます。 (注:provideが呼ばれると、requireのautoprovide機能は 抑制されるため、X.scm中で(provide "X")も指定する必要があります。)

    もちろんの方法では、ユーザが (require "Y")(require "X") より先に書いてしまったらY.scmはロードされてしまいます。 この方法は、現場において、永久的な解決が高コストになるような場合のとりあえずの 回避処置と考えるべきでしょう。

  • featureを全くprovideしない。 feature引数に#fを渡すことで、何のfeatureもprovideすることなく requireによるautoprovidingを抑制できます。

    これもまた、何らかの一時的な解決と考えるべきです。たとえば開発中に、 X.scmを頻繁に変更するために(require "X")が常に そのファイルをロードするようにさせたい、といった場合が考えられます。 X.scmのリリースの前に (provide #f) を消しておくことを 忘れないように。また、インタラクティブにリロードしたいならば gauche.reload (gauche.reload - モジュールの再ロード参照) を使うことを おすすめします。

Function: provided? feature

featureが既にprovideされていれば#tを返します。


6.22.4 Autoload

Macro: autoload file/module item …

item … がオートロードされるように設定します。すなわち、 次にitemが参照された時、それが実際に評価される前にfile/moduleがロード されるようにします。これによって、必要とされる時までfile/moduleのロードを遅らせる ことができます。

引数file/moduleには、文字列かシンボルを指定できます。文字列の場合は そのファイルがロードされます。シンボルの場合、その名のモジュールが(useと 同じルールで)ロードされ、itemfile/moduleから オートロードを呼び出したモジュールへとインポートされます。 (モジュールのuseの詳細についてはモジュールの定義と選択を参照して下さい)。

itemは変数名(シンボル)か、(:macro symbol)というフォーム でなければなりません。変数名の場合は、その変数が評価されようとした時に 指定のファイル/モジュールがロードされます。後者のフォームの場合は、 (symbol arg …) というフォームがコンパイルされようとした 時に指定のファイル/モジュールがロードされます。後者はマクロのオートロードになります。

symbolfile/moduleの中で定義されなければなりません。 そうでなければロード時にエラーが報告されます。

手続きのオートロードの例を示します。

(autoload "foo" foo0 foo1)
(autoload "bar" bar0 bar1)

(define (foobar x)
  (if (list? x)
      (map bar0 x)
      (foo0)))

(foobar '(1 2)) ; この時点で "bar" がロードされる

(foobar #f)     ; この時点で "foo" がロードされる

マクロのオートロードを設定した場合、指定のファイルまたはモジュールは、 そのマクロを使っているフォームが実行されるされないにかかわらず、 コンパイラがそのフォームを見た時点でロードされることに注意してください。


6.22.5 ライブラリの操作

ある特定のライブラリおよび/あるいはモジュールがシステムにインストールされ ているかどうかをチェックするための手続きがいくつかあります。

以下の説明の中では、patternはシンボルまたは文字列です。シンボル である場合にはモジュール名(たとえば、foo.bar)を指定します。 文字列である場合にはライブラリの部分パス(たとえば、"foo/bar")を 指定します。これはライブラリサーチパス以下で検索されます。 patternにはglobのメタ文字と同じ意味で *? を 使うこともできます。

Function: library-fold pattern proc seed :key paths strict? allow-duplicates?

ライブラリ/モジュールファイルに対する基本イテレータ。この手続きは pattern にマッチする Scheme のプログラムファイルを検索します。 検索は paths (デフォルトは標準のファイルロードパス、 *load-path*)にリストされたディレクトリ以下でおこなわれます。 マッチしたファイル毎に、proc が、そのマッチしたモジュール名あるいは ライブラリ名、そのプログラムファイルのフルパス、状態値、の3つの引数を ともなって呼びだされます。seedは、初期状態値として使われ、 proc が返す値は次の proc の呼出し時に状態値として使われます。 最後の proc から返された値は library-fold の値として返ります。

pattern がシンボルでかつ、キーワード引数 strict?#t (デフォルト値)であれば、この手続きは あたえられたモジュール名のパターンにマッチするようにみえるファイル名に たいして library-has-module? を適用して、 本当にそのモジュールを実装しているファイルを見付けます。 大量のモジュールにマッチさせようとすると、時間がかる可能性があります。 #fstrict? に渡すことで、余分なチェックを回避できます。 pattern が文字列だった場合、照合はファイル名に対してのみ行われ、 strict?は無視されます。

デフォルトでは、path 中の pattern にマッチする同じ名前をもつ 2つ以上のファイルがあるばあい、path に最初に出現したものだけが 採用されます。そのライブラリに対して、require あるいは use を用いたをつかった場合に得られるのはこのファイルです。 すべてのマッチしたファイルについて反復したければ、allow-duplicates? キーワード引数に #t を渡します。

(library-fold 'srfi.1 acons '())
 ⇒ ((srfi.1 . "../lib/srfi/1.scm"))

(library-fold "srfi.1" acons '())
 ⇒ (("srfi.1" . "../lib/srfi/1.scm"))

;; acons が呼ばれるのとは逆順のリストが
;; 返ることに注意してください
(library-fold 'srfi.1 acons '() :allow-duplicates? #t)
 ⇒ ((srfi.1 . "/usr/share/gauche-0.98/0.9.13/lib/srfi/1.scm")
   (srfi.1 . "../lib/srfi.1.scm"))

;; 利用可能な dbm の実装を見付けます
(library-fold 'dbm.* acons '())
 ⇒ ((dbm.cdb . "/usr/share/gauche-0.98/0.9.13/lib/dbm/cdb.scm")
   (dbm.gdbm . "../lib/dbm/gdbm.scm")
   (dbm.ndbm . "../lib/dbm/ndbm.scm")
   (dbm.odbm . "../lib/dbm/odbm.scm"))
Function: library-map pattern proc :key paths allow-duplicates? strict?
Function: library-for-each pattern proc :key paths allow-duplicates? strict?

マッチしたライブラリ/モジュール上のイテレータの map版および for-each版。照合操作とキーワード引数の詳細については上述の library-foldを参照してください。

procは、マッチしたモジュール/ファイル名と、そのファイルの フルパスの 2 つの引数をうけとります。library-for-each は 結果を捨てます。

(library-map 'srfi.4 list :allow-duplicates? #t)
 ⇒ ((srfi.4 "../lib/srfi/4.scm")
            (srfi.4 "/usr/share/gauche-0.98/0.9.13/lib/srfi/4.scm"))

(library-map 'dbm.* (lambda (m p) m))
 ⇒ (dbm.odbm dbm.ndbm dbm.gdbm dbm.cdb)
Function: library-exists? mod/path :key paths force-search? strict?

mod/path で指定されたライブラリあるいはモジュールを検索し、 もしあれば、真値を返します。キーワード引数 paths および strict?library-fold のそれと同じ意味です。

上述のイテレータ手続きとはちがい、この手続きは呼び出しの過程で 最初に既にロードされているライブラリおよびモジュールをチェックします。 もしそのときに mod/path を見つけたら、真値を返し、ファイル システムを見にいくことはありません。キーワード引数 force-search?#t を渡せば、すでにロードされいるライブラリおよびモジュール のチェックはスキップされます。

Function: library-has-module? path module

path で指定したファイルが存在し、かつ、module で名指しされた モジュールが実装されている場合でその場合に限り、#t を返します。 path は実際のファイル名でなければなりません。

(library-has-module? "./test/foo/bar.scm" 'foo.bar)
 ⇒ #t ;; if ./test/foo/bar.scm implements module foo.bar.

この手続きは典型的なソースコードの配置を仮定して、与えられたファイルが そのモジュールを実装しているかどうかを決定します。すなわち、 まずそのコードのフォームを読み、与えられたモジュールを定義している define-module フォームかどうかを見ます。



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