ソースファイル(=モジュール)と同名で拡張子が .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ファイルを作り,意図したのと違うことになる。