ソースファイル(=モジュール)と同名で拡張子が .mli のファイルを用意しておくことで,モジュールにシグネチャを与えることができる。
前にも例に挙げた Table モジュールを例にとると,
table.mli
type ('a, 'b) t val empty : ('a, 'b) t val add : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t val retrieve : 'a -> ('a, 'b) t -> 'b option val dump : ('a, 'b) t -> ('a * 'b) list
table.ml
type ('a, 'b) t = Empty | Entry of 'a * 'b * ('a, 'b) t let empty = Empty let add key content table = Entry (key, content, table) let rec retrieve key = function Empty -> None | Entry (key', content, rest) -> if key = key' then Some content else retrieve key rest let rec delete key = function Empty -> Empty | Entry (key', content, rest) -> if key = key' then delete key rest else Entry (key', content, delete key rest) let rec dump = function Empty -> [] | Entry (key, content, rest) -> (key, content) :: (dump (delete key rest))
こういう風にtable.mliとtable.mlを書く。これで
module type TABLE = sig ... end module Table : TABLE = struct ... end
と書いたのと同じになる。
コンパイルするときは,インターフェイス(mli)ファイルを先にコンパイルする。手順としては
- 各mliファイルを ocamlc でコンパイル(cmiファイルができる)
- 各mlファイルを ocamlc -c でコンパイル(cmoファイルができる)
- 全cmoファイルを ocamlc でリンク
となる。mlファイルよりもmliファイルを先にコンパイルしておくことに注意。でないとcmiファイルがないので,コンパイラが推論して勝手にcmiファイルを作り,意図したのと違うことになる。