ここまでのモジュールの例は全て対話式システム上での例でした。 しかし、モジュールはより大規模なバッチコンパイルされるプログラムに対してもっとも有効です。 そのような類のプログラムでは、ソースを翻訳単位と呼ばれる個別にコンパイルできるいくつかのファイルに分割し、変化があった場合に再コンパイルが必要になる範囲を最小化しなければならない実用上の必要があります。
Objective Camlでは、翻訳単位はストラクチャやシグネチャの特殊なケースで、モジュールシステムの言葉で簡単に説明できます。
翻訳単位 A
は二つのファイルからなります
A
.ml
。 struct
... end
の内部と同様に定義の列を含むA
.mli
。 sig
... end
の内部と同様に仕様の列を含む
両ファイルによりストラクチャ A
が、トップレベルで次のように入力されたように定義されます。
module A: sig (* ファイル A.mli の内容 *) end = struct (* ファイル A.ml の内容*) end;;
これら翻訳単位を定義するファイルは個別に ocaml -c
コマンドによりコンパイルできます。(-c
オプションは「コンパイルのみ行いリンクを試みない」という意味)
コマンドの結果、コンパイルされたインターフェースファイル(拡張子 .cmi
)やコンパイルされたオブジェクトファイル(拡張子 .cmo
)が生成されます。
全ての翻訳単位がコンパイルされたとき、それら .cmo
ファイルは ocaml コマンドを使いリンクされます。
例えば次の一連のコマンドは二つの翻訳単位 Aux
と Main
からなるプログラムをビルドしています。
$ ocamlc -c Aux.mli # produces aux.cmi $ ocamlc -c Aux.ml # produces aux.cmo $ ocamlc -c Main.mli # produces main.cmi $ ocamlc -c Main.ml # produces main.cmo $ ocamlc -o theprogram Aux.cmo Main.cmo
得られたプログラムは、トップレベルで次のように入力された場合とまったく同様に振舞います。
module Aux: sig (* Aux.mli の内容*) end = struct (* Aux.ml の内容 *) end;; module Main: sig (* Main.mli の内容 *) end = struct (* Main.ml の内容 *) end;;
特に、 Main
は Aux
を参照することが出来ます。
Main.ml
及び Main.mli
に含まれる定義や宣言からは Aux.
記法により ident
Aux.mli
でエクスポートされた定義にアクセスすることが出来ます。
リンク時に ocaml コマンドに与えられる .cmo
ファイルの順番は、その順番に各モジュールが定義されたものとして解釈されます。
そのため、この例では Aux
が最初に現われているので Main
から Aux
を参照できますが、 Aux
から Main
は参照できません。
ここで注意が必要なのは、トップレベルのストラクチャのみが分割コンパイルされるファイルに対応させることが出来、ファンクタやモジュール型はそのままでは分割コンパイルされるファイルにすることが出来ないということです。 しかしながら、全てのモジュール類のオブジェクトはストラクチャの中におくことが出来るため、解決策としてはファンクタやモジュール型をストラクチャの中に置くことになります。 このストラクチャはファイルに対応させることができます。