Overview

OCaml 開発環境について ~ OCaml コンパイラソース付属ツール

この文章では 2012年3月での関数型言語 OCaml によるプログラム開発における、必須・重要ツールを紹介する。 まず、OCaml コンパイラ一式をインストールした際に付属する「公式」ツール群から解説する。

★は重要度。五点満点。

OCaml コンパイラ

現時点での公式最新バージョンは 3.12.1。

次のリリースバージョンはおそらく 4.00.0。開発中の trunk バージョンは 4.01.0。 4.00.0 では GADT と 3.12.1 より使い易い first class module が入っている。 2012年内のリリース予定だと思われる。

このところ OCaml のマイナーバージョンの変わるリリースは一年に一度位。 バグフィックスパッチやリリースはより頻繁にある。

ocaml toplevel ★★★★

OCaml 対話環境。いわゆる REPL(Read-Eval-and-Print Loop)。 入力は型推論の後 bytecode へとコンパイルされ VM により評価される。 Bytecode とはいえコンパイルが入るため、インタープリタとは普通呼ばれない。

Real World OCaml programmer は toplevel はまず使わない。

ocaml toplevel の対話環境としての能力はあまり高くない。行の編集や履歴を呼び出したい場合は、

  • rlwrap : read line wrapper
  • emacs の shell mode 内などでの実行

で編集能力を強化するのが普通である。

(Native code へとコンパイルする ocamlnat という対話環境も存在する。ただしまだ「非公式」)

ocamlc bytecode compiler ★★★★★

OCaml ソースコードを bytecode へとコンパイルするコンパイラ。 Bytecode プログラムは native code と比べると遅いが、 ocamldebug を使ったデバッグが可能。

ocamlopt native code compiler ★★

OCaml ソースコードを native code (マシン語)へとコンパイルする。 Native code がサポートされているアーキテクチャで OCaml コンパイラソースコードディレクトリで make opt すると作成される。 実はほとんど使わない。次の ocamlc.opt, ocamlopt.opt を参照のこと。

ocamlc.opt ocamlopt.opt ★★★★★

Native code にコンパイルされた bytecode および native code コンパイラ。 Native code コンパイルが可能な環境では通常このコンパイラを使う。

OCaml コンパイラソースコードで make opt の後に make opt.opt を行うと作成される。 通常の ocamlc, ocamlopt は bytecode で実行される。 *.opt コンパイラは native にコンパイルされているため bytecode へとコンパイルされたコンパイラより実行速度が早い。 (Bytecode 版コンパイラがひどく遅いわけではない。)

ocamlc, ocamlopt 以外のツールにも、 .opt の postfix がついた native code バージョンが存在する。

ocamldep ★★★★★

ocamldep は複数の OCaml プログラムファイル間の依存関係を抽出するツール。 結果は Makefile の依存書式で出力される。通常は、 ocamldep *.ml *.mli > .depend として依存情報をファイルに書きだし、それを Makefile 等で include する。

使い方の例は、 Makefile を使った OCaml ソフトウェアを見れば、まず使用されているので、それらを参考に。

OCaml パーサーツール

OCaml では lex-yacc スタイルのパーサツールが標準で付属しており、 このパーサによって OCaml の文法自体も定義されている。

ocamllex ★★★★

Multi-byte char を処理する場合は、 ulex を使うべきである。

Lexical analyser。 Lex のスタイルを踏襲しつつ、アクション等のコードを OCaml プログラムで記述できる。 そのため、基本的に lexer (字句解析)や正規表現の知識が有用かつ前提である。 ocamllex は *.mll というアクション等のコードを OCaml プログラムで記述できる。 *.mll の例は OCaml コンパイラソースの parsing/lexer.mll を参考にするといい。

ocamlyacc ★★★★

ほとんど上位互換で、エラーメッセージの読みやすい Menhir を使うべきである。

Parser generator。こちらは yacc のスタイルを踏襲し、アクション等のコードを OCaml プログラムで記述できる。 そのため、 yacc の知識が必要。例えば shift-reduce, reduce-reduce の知識がなければ使いこなせない。 ocamlyacc は *.mly という拡張子のファイルを受け取り、 parsing rule を解釈し、 *.ml へと変換する。 注意すべき点として、 OCaml コード以外のパートでのコメントは (* ... *) ではなく、 /* ... */ であることが挙げられる。 *.mly の例は OCaml コンパイラソースの parsing/parser.mly を参考に。

ocamllex, ocamlyacc は色々と古臭い部分もあり、イライラすることもあるが、 ほとんどアップデートもなく、非常に良く枯れているツールであるともいえる。

ocamlyacc のほぼ上位互換 parser generator として Menhir という外部ツールがある。 Menhir は ocamlyacc と同じ *.mly ファイルを受け取る上に、エラーメッセージが読みやすいなど良い点が多い。そのため、現在 OCaml で parser generator を使う場合は Menhir を使うことが推奨されている。 (ユーザに Menhir をインストールさせるのが面倒だと思われる場合は、 Menhir で新しい機能を使わず、デバッグ開発を行い、リリース時には ocamlyacc に戻す、ということも可能。)

Camlp4 pre-processor and pretty printer ★★★

Camlp4 (略称P4) は Pre-Processor and Pretty Printer の4つの P から P4 と呼ばれ、 自分でパーサーをスクラッチから記述できるだけでなく、 OCaml コードでのマクロや文法拡張を実現することもできる強力なツール。 P4 は yacc のような LALRベースではなく、 LLベースの stream parsing が使われるため、 ocamlyacc と camlp4 では文法記述法が異なる。

P4 では OCaml の文法が stream parsing で再定義されており、 このパーサーを使って OCaml プログラムを解釈し、その結果を OCaml コンパイラに送ることができる (OCaml 標準の lex-yacc で書かれたパーサーは迂回される)。 使い方は OCaml コンパイラの -pp オプションを見ること。

この P4 の OCaml parsing rule群を動的に変更することで、 OCaml の文法を拡張することができ、 単純なマクロから、非常に複雑なメタプログラミングまで P4 が活躍する。 高レベルな OCaml プログラミングでは、P4 を利用した複数の文法拡張をしばしば利用するため、 複雑ではあるが非常に重要なツールである。 文法拡張記述には OCaml の通常の文法 (original syntax) と OCaml 文法拡張を書く際、 ambiguity が少なくなる改良文法 (revised syntax) の二つの文法を 選ぶことができる。これらの文法を使うかどうかに対応して Camlp4 コマンドも camlp4* から始まる複数のコマンド群からなる。

問題は、 P4 は非常に複雑なツールであるため、 OCaml 本体の正式文法が拡張された場合、それへの追随が遅れることがあること (よって、新機能と P4 の組み合わせが使えなかったりする)、そして、ドキュメントがほとんど整備されていないこと、である。 既存の P4 で書かれた文法拡張を使うだけの場合は P4 でのパーサルールの書き方などを理解する必要はないとはいえ改善が望まれる。

以下は 3.10系 Camlp4 を開発した人が書いた情報。残念ながら全く不十分

3.10系 P4 のチュートリアルとしては Jake Donham の Reading Camlp4 http://ambassadortothecomputers.blogspot.com/search/label/camlp4 は素晴らしい記事であり、推薦する。

インターネット上の P4 の情報を調べる際は、必ずそれがいつの時期に書かれたものか確認すること。

Camlp5 との関係

Camlp4 とは別に Camlp5 というツールが存在するが、これは OCaml に同梱されていない。

Camlp5 は OCaml version 3.09 まで同梱されていた Camlp4 が引継がれたもので、 Version 3.10 以降の OCaml 同梱の Camlp4 はリライトされ、その際に大幅に refactoring されている。 そのため、コードベースとしては 「3.09 までの P4」 および P5 は似ている。 3.10系 P4 は それらからかなり離れている。 P5 が P4 より数字が多いため、優れているとか、その逆、という関係ではない。 なお、 P5 は Coq theorem prover でよく使用されている。

基本的にこれら P4, P5 のアイデアは同じなので 古い P4 および P5 のドキュメントを読んで 3.10系 P4 の基本的な使用方法を理解することは可能であるが、その際には必ず 3.10系 P4 の working example などを参照して細かな違いを把握する必要がある。

P4 と P5 が何故ブランチしたか、はさまざまな事情があるがここで語るべきではない。

ocamlmktop, ocamlmklib ★★★

まだ書いてない

ocamldoc ★★

まだ書いてないよく知らない

ocamlbuild ★★★

まだ書いてないよく知らない

エディタ支援

公式ソースコードに付属するエディタ支援は Emacs, XEmacs の物に限られる (Emacs24 動作未確認) ソースコードからビルドしている場合、 make install ではこれらの elisp ファイルはインストールされない。 導入にはソースディレクトリ/emacs/README を読むこと。

caml.el ★★★★★

OCaml プログラムのインデントとハイライトを提供する Caml-mode を提供する。 外部ツールである tuareg-mode を好む人もいる。

caml-types.el ★★★★★

任意の部分式の型を表示させることで型エラー解消などの作業を効率的に行うためのツール。

OCaml コンパイラ(ocamlc, ocamlopt)に -annot オプションを付けて *.ml, *.mli ファイルを コンパイルすると *.annot というファイルができる。この *.annot ファイルにはソースコード 上の場所をキーとして、そこにある式の型などの情報が記録されいる。 caml-types.el はこのファイルを走査し、部分式の型を Emacs のメッセージバッファに表示する。

caml-types.el は caml.el と独立しており、 tuareg-mode と一緒に使うこともできる。

caml-debug.el ★★

ocamldebug を Emacs で使うための elisp。現在実行中のソースコードの場所などを Emacs 中に表示できる。

ほとんど使用されないツール

バイトコードデバッガ ocamldebug ★

ごくたまに利用される程度である。

ocamldebug は OCaml の byte code プログラムのためのデバッガ。 ocamldebug を使うためには各バイトコードオブジェクトファイル *.cmo を ocamlc にデバッグフラグ -g を付けてコンパイルする必要がある。

ocamldebug では一旦進めたデバッグステップを巻き戻すことができるという、ちょっと変わった機能がある。とは言え… printf デバッグか、 gdb を使った native code プログラムのデバッグの方が判りやすい場合が多い。どうしてもプログラム挙動がわからない場合、念のために使われることが多い。これは ocamldebug が非力だからというのではなく、やはり静的に型付けされた関数型プログラムではキャストの誤りや NULL エラーが起こることがなく、あまりデバッグを必要としないからというのが大きい。

バイトコードプロファイラ ocamlprof と ocamlcp ★

ほとんど利用されない。

ocamlprof は byte code プログラムのためのプロファイラ。 ocamlprof を利用するためには各バイトコードオブジェクトファイルは ocamlcp という ocamlc のラッパを用いてコンパイルされていなければならない。

ocamlcp でコンパイルされた byte code 実行ファイルを実行すると ocamlprof.dump というファイルが作成される。 これを ocamlprof コマンドを使って関数などの使用回数を抽出、 元のソースファイル内にコメントとして書きだす。

ocamlprof は呼び出された回数しかプロファイルしないので利用されない。

OCaml プログラムでプロファイルを取る場合は、通常 ocamlopt に -p オプションを付けて native code でのプロファイルを取り、 そのアプトプットを gprof で可視化するのが普通である。

マニアックなツール

expunge

ocamlobjinfo