Diary?

2009-02-25
Wed

(19:58)

とりあえず OCaml のコンパイラの基本的な使い方とモジュールの基礎までやってみた。まず OCaml はバイトコードとネイティブコードの二種類の出力形式がある、というかコンパイラが二種類ついてくるが、俺は今のところネイティブの方しか使ってない。バイトコードで出力すると OCaml の対話型インタープリタでロードしていろいろできるが、残念ながらこのインタープリタの操作性は GNU Readline モジュールを使っていないせいで非常に悪い。これはライセンス上の理由で使えないせいのだと言われているけど、今確認したら OCaml って QPL と LGPL のデュアルライセンスになってたぞ。

まあそれはおいといて、まずはコンパイラの使い方だが、基本的には

  1. "ocamlopt -c foo.mli" でシグネチャを先にコンパイル(.mli ファイルを作ってある場合ね)
    • 出力は .cmi ファイル
  2. "ocamlopt -c foo.ml" でソースファイルをコンパイル
    • 出力は .cmx ファイル と .o ファイル
  3. 上記の手順を繰り返す
  4. "ocamlopt foo.cmx bar.cmx..." でリンクして終了
    • デフォルトの実行ファイルの名前は a.out

このとき標準でないライブラリを参照している場合、その都度昨日の日記に書いたようにライブラリを指定してあげること。この辺りはいちいち手でコマンドを打っていられないので、 Makefile(最近は OMake?)でビルドの手順を自動化しておかないとやってられない。あと出力されるファイルの数が結構な数になるけど、それぞれのファイルの意味はちゃんとオフィシャルのリファレンス・ヘルプで確認すること。

いい加減モジュールシステムに移ろう。先に書いておくと、俺は OCaml のモジュールシステムは割合良さそうに思える。まず OCaml のモジュールの基本単位はファイル。ディレクトリはあまり関係なくて、あくまでもファイル。ファイルの名前は特に規定は無いっぽいけど、ファイル名の先頭を小文字にするのが一般的な命名規約らしいのでそれに従うことにしよう。ここでは foo.ml というファイルを作り、以下のようなコードを書いたとする。

let sum x y = x + y;;

この時自動的にファイル名の先頭を大文字にした名前がモジュール名として使用されるので、この場合は Foo モジュールが作られ、その中に sum 関数が定義された状態となる。そのため、もしも次のように書いてしまうと Foo モジュールの中の Foo モジュールという階層ができてしまう。

module Foo =
struct
    let sum x y = x + y;;
end;;

「入門 OCaml」ではモジュールについての説明の頭の方にファイル名とモジュールの関係が出てこなくて、あくまでも対話型インタープリタ上でのコーディングしかされておらず、ファイル名がモジュール名になるとかの話が出てくるのはファンクターの説明が終わったあと。これはあまりいい構成ではないと思うぞ俺は。

ちなみに同じようなことはシグネチャにも言えるので、先の Foo モジュールのシグネチャは以下のように書ける。

val sum : int -> int -> int

ocamlopt -i で .ml ファイルのシグネチャ一覧が出力されるので、それを元に .mli ファイルを作ると楽かもしれない。まあ、全部の関数や型を外部に公開する場合はそもそも .mli ファイルはいらないんだが。

あとさっき「ディレクトリはあんま関係ない」と書いたけど、それについて少し。例えば hoge というディレクトリに fuga.ml というソースコードを置いてあるとする。これが Python なんかだとライブラリのサーチパスから hoge ディレクトリが見える場合、モジュールへのアクセスは hoge.fuga のようになる。それに対して OCaml は fuga.ml のモジュールはあくまでも Fuga であり、そもそもライブラリのサーチパスから hoge ディレクトリが見えているだけではダメ。仮に hoge ディレクトリがライブラリのサーチパス(/usr/lib/ocaml とか)直下にあっても、それはサーチパス上にあることにはならない。

なので明示的に hoge ディレクトリをコンパイルオプションなりなんなりでサーチパスに加え、その上で fuga.cmx をリンクするファイルに加え、それで Fuga モジュールを使うというスタイルになる。とりあえず以下のようなファイル・ディレクトリ構成を考える:

hage.ml
hoge/
    fuga.ml

それぞれのソースファイルの中身はこんな感じ:

(* hage.ml *)
print_int (Fuga.mul 1 2);;

(* fuga.ml *)
let mul x y = x * y;;

この時 hoge.ml をコンパイルするまでの手順は簡単には以下の通り。

$ ocamlopt -c hoge.fuga.ml
$ ocamlopt -I hoge fuga.cmx hage.ml

もしも「Hoge の中の Fuga モジュールという定義にしたい」というのであれば、それはモジュールの include あたりを使えばできる。

モジュールについてはいろいろ面白い機能があるみたいだけど、それについてはまた別の日に。

Creative Commons
この怪文書はクリエイティブ・コモンズ・ライセンスの元でライセンスされています。引用した文章など Kuwata Chikara に著作権のないものについては、それらの著作権保持者に帰属します。