Objective Caml では,バッチコンパイラ ocamlc を用いて Objective Caml
プログラムを書いたファイルから,実行可能ファイルを生成することができる.
また,プログラムを複数のファイルに分割してコンパイルすることも
できる.このとき,分割された単位をモジュールと呼ぶ.
Objective Caml ライブラリもモジュールの形で提供されており,
その使用方法は同じである.
8.1 ライブラリモジュールの使い方
まずは,ライブラリの使用法を学びながら,既存のモジュールの
使い方をみてゆく.Objective Caml のライブラリは,List, Array, Sort
モジュールなどのデータ構造に関するもの,Printf などの入出力に
関連するもの,Sys モジュールなどの,OSやObjective Caml 処理系とのインター
フェースをとるためのもの等が豊富に用意されている.ひとつひとつの
詳しい機能などはマニュアルの19章をみてほしい.ここでは List モジュールと
Queue モジュールを題材にする.
モジュール内の関数は,〈 モジュール名 〉.〈 関数名 〉 と言う形で
呼び出すことができる.
# List.length [5; 6; 8];;
- : int = 3
# List.concat [[4; 35; 2]; [1]; [9; -4]];;
- : int list = [4; 35; 2; 1; 9; -4]
以前にみてきたリスト操作のための関数 rev, append, map, fold_left,
fold_right 等は,ほとんど List モジュールに定義されている.
Queue モジュールは,いわゆる待ち行列の
データ構造を実装したもので,リストのように同種のデータをまとめて格納するの
に用いる.add, take という要素を追加,取り出す関数が
用意されているが,特徴は「先入れ先出し」であり,add した順番でしか
take できない.
Queue モジュールはモジュール内で t という名前の独自の型を定義している.こ
の場合には,その型にもモジュール名がついた形 'a Queue.t で表される.
# let q = Queue.create ();;
val q : '_a Queue.t = <abstr>
# Queue.add 1 q; Queue.add 2 q;;
- : unit = ()
# Queue.take q;;
- : int = 1
# Queue.take q;;
- : int = 2
# Queue.take q;;
Exception: Queue.Empty.
最後に発生した例外 Empty は Queue モジュール内で定義されたものである.
open 宣言
同じモジュールを使い続けると,いちいちモジュー
ル名をつけるのが面倒になってくる.open 宣言は文字通りモジュールを
「開く」もので,モジュール内の定義がモジュール名なしでアクセスできるよ
うになる.
# open List;;
# length [3; 9; 10];;
- : int = 3
この open 宣言は,open した時点ですでに宣言されている同名の定義を
隠してしまうので,同名の定義を提供する複数のモジュールを開くときには
順番に注意した方がよい.隠されてしまった名前は,結局モジュール名つきの
記法でアクセスすることになる.
8.2 バッチコンパイラによる実行可能ファイルの生成
もっとも単純な ocamlc の使用法は,Objective Caml
の宣言を書いたファイル(拡張子は .ml)を用意して,
シェルのコマンドラインから
ocamlc -o 〈 出力ファイル名 〉 〈 ソースファイル名 〉
としてコンパイラを 起動すると,〈 出力ファイル名 〉
という実行可能なファイルが生成される. -o オプションを省略すると
a.out という名前で生成される.
igarashi@zither:text> cat hello.ml
let _ = print_string "Hello, World!\n"
igarashi@zither:text> ocamlc hello.ml
igarashi@zither:text> a.out
Hello, World!
igarashi@zither:text> cat fact.ml
let rec fact n =
if n = 0 then 1 else n * fact (n - 1)
let _ = print_int (fact 10)
igarashi@zither:text> ocamlc -o fact10 fact.ml
igarashi@zither:text> ./fact10
3628800igarashi@zither:text>
バッチコンパイルされるファイルの中には宣言の並びだけが許され,
インタラクティブコンパイラで見たような,式だけからなるものは
はじかれるので,
let _ = ...
のような,式を評価して結果を捨てる宣言として記述している.
(C の main 関数に相当するものがなく,ただ単に上から評価を行っていく.)
さて,最初に述べたようにソースファイルは複数のファイルに分割することが
できる.Objective Caml システムでは,一つ一つのソースファイルがモジュールに
対応し,UNIX 上でのファイル名の先頭を大文字にしたものが,Objective Caml での
モジュール名になる.例えば,
foo.ml のソースファイル中の宣言は,他のファイルからは,モジュール
Foo にあるものとしてアクセスされる.下は,fact.ml と main.ml
である.
igarashi@zither:samples> cat fact.ml
let rec fact n =
if n = 0 then 1 else n * fact (n - 1)
igarashi@zither:samples> cat main.ml
(* main.ml *)
let _ =
print_int (Fact.fact 10);
print_newline();
main.ml では Fact モジュール内の fact 関数を Fact.fact という
名前で使用している.コンパイラには,二つのファイル名を,
依存されているものから順に並べる.
igarashi@zither:samples> ocamlc -o fact10 fact.ml main.ml
igarashi@zither:samples> fact10
3628800
また,-c オプションを用いると,各モジュールを個別にコンパイルするこ
とができる.中間的なオブジェクトファイルとして,.cmi という拡張子を
持つ,モジュールのインターフェース情報(モジュール内にどんな名前の関数
がどんな型で宣言されているかの情報・シグネチャとも呼ぶ)をコンパイルし
たファイルと,.cmo という拡張子を持つモジュール自体をコンパイルした
ファイルが生成される..cmi ファイルは,そのモジュールを使用するファ
イルがコンパイルされるときに必要であり,(.cmo 自体は必要ではない.)
下の例で,main.ml を先にコンパイルすることはできない.そして,.cmo
はあとでリンクして実行可能ファイルを生成することができる.
igarashi@zither:samples> ocamlc -c fact.ml
igarashi@zither:samples> ocamlc -c main.ml
igarashi@zither:samples> ocamlc -o fact10 fact.cmo main.cmo
各モジュールのインターフェースは -i オプションで標準出力に書き出すことがで
きる.
igarashi@zither:samples> ocamlc -i -c fact.ml
val fact : int -> int
プログラマはこれを見て,各関数に意図通りの型が与えられているかどうかを
確認することができる.