Source

ocaml-zippy-tutorial-in-japanese / ocaml-tools.rst

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

2012年12月での関数型言語 OCaml コンパイラ一式をインストールした際に付属する「公式」ツール群の紹介を行う。多岐に渡るので、一つ一つの詳しい説明は行わない。各ツールの細かい情報はそれぞれのドキュメントを参照して欲しい。

もし知らないツール名があったらちょっと読んでみて欲しい。もしかしたらあなたの問題を解決するツールがあるかもしれないから。(特に caml-types.el)

★は重要度。五点満点。

外部ツールの紹介はまた今度ね。

OCaml コンパイラ

現時点での公式最新バージョンは 4.00.1。 4.00.1 は基本的に 4.00.0 のバグフィクスリリース。 4.00.0 では GADT と 3.12.1 より使い易い first class module が入っている。

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

ocaml toplevel ★★★★

OCaml 対話環境。いわゆる REPL(Read-Eval-and-Print Loop)。 入力は型推論の後 bytecode へとコンパイルされ VM により評価される。 Bytecode とはいえコンパイルが入るため、インタープリタとは普通呼ばれない。 (Native code へとコンパイルする ocamlnat という対話環境も存在する。 ただしまだ「非公式」)

ocaml toplevel にはラインエディタ機能はない。行の編集や履歴を呼び出したい場合は、

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

Real World OCaml programmer で toplevel を使うか使わないか…は人により違うようだ。 Toplevel は値のプリンタがあるので、ライブラリをロードして関数のインタラクティブなテストを行う人はいる。 一方、私は電卓として使うか型システムの挙動を確かめる時以外全く使わない。

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 にコンパイルされているため ocamlc, ocamlopt より実行速度が早い。 (Bytecode 版コンパイラがひどく遅いわけではないが。)

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

依存抽出: ocamldep ★★★★★

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

使い方の例は、 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 を参考に。 なおその場合は完全に shift-reduce 警告を 0 にしている所を味わうこと。

ocamllex, ocamlyacc は色々と古臭い部分もあり、イライラすることもあるが、 ほとんどアップデートもなく、非常に良く枯れており高速に動作する。 Lex-yacc も使えずに Parsec があーたらどーたらカッコイイとか構成的に書けるとか つぶやくのは甘えです。

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

マクロ/文法拡張システム: Camlp4 pre-processor and pretty printer ★★★★

Camlp4 (略称P4) は Pre-Processor and Pretty Printer の4つの P から P4 と呼ばれ、 自分でパーサーをスクラッチから記述できるだけでなく、 OCaml コードでのマクロや文法拡張を実現することもできる強力なツール。 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* から始まる複数のコマンド群からなる。

CamlP4 は OCaml 3.10 同梱版より完全にリライトされ、細かい部分がかなり変更された。そのため、「3.10以前系」のチュートリアルドキュメントは「3.10以降系」には細かい点では違いが多すぎて役に立たない。そして、P4 について日本語/英語で書かれたウェブ上のドキュメントはほとんどが「以前系」についてである。「以降系」のドキュメントはあまりない。 基本的なアイデアは以前系も以降系も同じなので 古い P4 のドキュメントを読んで 以降系 P4 の基本的な使用方法を理解することは可能であるが、その際には必ず 3.10系 P4 の working example などを参照して細かな違いを把握する必要がある。既存の P4 で書かれた文法拡張を使うだけの場合は P4 でのパーサルールの書き方などを理解する必要はないとはいえ改善が望まれる。

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

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

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

Camlp5 との関係

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

Camlp5 は 3.10以前系の Camlp4 が引継がれたもので、コードベースとしては 「3.09 までの P4」 および P5 は似ている。 3.10系 P4 はそれらからかなり離れている。 P5 が P4 より数字が多いため、優れているとか、その逆、という関係ではない。 なお、 P5 は Coq theorem prover でよく使用されている。

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

リンク支援: ocamlmktop, ocamlmklib ★★

ocamlmktop および ocamlmklib は外部Cライブラリをリンクした toplevel や ライブラリを作成する際に補助的に使用するツール。

これらのライブラリや toplevel は OCaml コンパイラ、C コンパイラ、リンカ、アーカイバ を自分で呼び出すことで 作成できるのだが、この煩雑な作業を代行してくれるのが ocamlmktop と ocamlmklib である。

プログラムビルドシステム: ocamlbuild ★★★★

プログラムビルドシステム。

ocamlbuild は簡単な OCaml ソースに対しては ソースファイル名を列挙するだけでモジュール間の依存関係解析からコンパイル、リンクに至るまでを自動的に行なってくれる。そのため Makefile のような既存の外部ビルドシステムにおけるビルドの煩雑さから解放される。

複雑なソース、プログラムコードの自動生成や特殊なリンクが必要な場合など、の場合は myocamlbuild.ml という OCaml モジュールで特殊ルールを記述し ocamlbuild に伝える必要がある。このファイルでは ocamlbuild が提供するルール記述用ライブラリを使うことができる。問題はこのライブラリを使うドキュメントがあまり整備されていないこと。(Camlp4 3.10以降系といい、 ocamlbuild といい 3.10 周りで作られたツールはドキュメントが全くなっていない) また、ルール記述が OCaml という汎用言語で書かねばならないためどう見ても Makefile や OMakefile などのビルドに特化した言語に比べ煩雑に見えてしまうことである。もちろん OCaml の利点である型安全性やパターンマッチ、高階関数などによってビルドルールを構成的に書くことができるのだが…もう少し文法拡張などして DSL の風味を付け加えるべきではなかろうか。

私は ocamlbuild は使わない。現在のところ OMake を使っている。とは言え、どうやら世の中的には ocamlbuild が標準になりつつあるのでそろそろ手を出さねばならない…

ドキュメントシステム: ocamldoc ★★★

OCaml のコードを HTML や LaTeX の形に抽出するためのドキュメントシステム。 (** ... *) という特殊なドキュメントコメントを使うことで簡単な整形記法や コード要素に明示的に結び付けられたドキュメントを簡単に書くことができる。

OCaml の標準ライブラリリファレンスドキュメントも ocamldoc によって 各 *.mli ファイルから自動的に生成されている。 (逆に言えば、ライブラリリファレンスをブラウザでアクセスせずとも *.mli を 読めば同じ情報が手に入る。)

エディタ支援

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

caml.el ★★★★★

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

caml-types.el ★★★★★

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

OCaml はその型推論のおかげでプログラム中に型を書く必要がほとんどない。そのため複雑なコードも簡潔に、かつ型安全に書くことができる。反面、型を明示的に書くことでプログラムが読みやすくなることもある。型が書かれていないため読みにくい他人の書いたコードや、型エラーが発生したがどうも何がおかしいのかわからない、といったことが起こり易くもなる。 caml-types.el を使えば OCaml コードの部分式の型を例えば明示されていなくともコンパイルの結果から表示させることができる。 caml-types.el を使っているかいないかで OCaml プログラマの生産性は数倍変わるので生き死にに関係する。

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

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

VIM ユーザは外部ツール ocaml-annot ( https://github.com/avsm/ocaml-annot ) などを使っているようである。

コード検索

OCamlBrowser ★★

型式や名前から関数や型定義を探し出す GUIツール。

例えば ('a * 'b) list を扱う関数って何がありますかねぇと思ったら ('a * 'b) list と入れて Type で検索するとそれらしい型を持つ関数が ずらっと表示される。 length って名前の関数はどんな型に定義されているのか知りたければ length と入れて Name で検索。そんな感じ。

OCaml のスタンドアローン Hoogle と言えば Haskell の人には判りやすいだろうが Hoogle より歴史は古い。 今は懐かしき Tcl/Tk を使用しているので入っていない環境も多いだろう。

これのWeb 版とも言える OCaml API Search (http://search.ocaml.jp )を使う という手もあるが、ocamlbrowser はスタンドアローンなのでローカルに インストールされたライブラリも探すことができる点は便利。

私は…使わないなー。どんな型に関する関数がどのモジュールで定義されているか だいたい頭に入っているから対応する *.mli ファイルをエディタで開いて 使うべき関数名や型コンストラクタを確認するくらいですんでしまう。

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

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

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

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

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

私は使わない…協調スレッドなどの実行順が判りにくいライブラリを使う場合デバッガではプログラムの実行を 人間 が追えないからだ。デバッガは追えていているのだが。

caml-debug.el ★

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

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

ほとんど利用されない。

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

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

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

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

マニアックなツール

ディレクトリ名がついている場合、それはインストールされないツールである。 OCaml をビルドするとその場所に実行ファイルができる。

./expunge
ライブラリ中のモジュールを外部から見えなくするためのツール。A というモジュールがライブラリにリンクされていれば、このライブラリを使うと外部から A という名前でこのモジュールにアクセスすることができる。 A を expunge すると、それができなくなる。コンパイラ屋さんくらいしか使わないツール。
ocamlobjinfo
オブジェクトファイルやライブラリ *.cm* ファイルの環境依存情報を見ることができる。OCaml ではオブジェクトファイル間の整合性は md5sum で管理されているので *.cmi の整合性が合わない!と言われ、これはコンパイラおかしいだろう!と感極まった場合に使うと良いかもしれない。
tools/dumpobj
*.cmo ファイルをダンプして VM opcode を眺めることができる
tools/read_cmt
OCaml 4.00.0 より -bin-annot オプションにより生成されるバイナリアノテーションファイル *.cmt をダンプしたり、 *.annot ファイルに変換することのできるツール。まあ ocamlspot を使えってこった