この章では Objective Caml のトップレベルシステムについて解説します。トップレベルシステムとは Objective Caml システムを read-eval-print のループを通して対話的に利用できるシステムです。このモードでは、システムは入力から Caml のフレーズを読み、型チェックし、コンパイルし評価して、導出された型と結果の値 (あればですが) を表示します。システムではフレーズを読む前に # (シャープ) プロンプトを表示します。
トップレベルに複数行にまたがった入力を与えることも可能です。入力は ;; (2 つのセミコロン) で終わります。トップレベルの入力には以下の文法で複数のトップレベルフレーズを与えることができます。
toplevel-input |
::= |
{ toplevel-phrase } ;; |
toplevel-phrase |
::= |
definition |
|
| |
expr |
|
| |
# ident directive-argument |
definition |
::= |
let [rec] let-binding { and let-binding } |
|
| |
external value-name : typexpr = external-declaration |
|
| |
type-definition |
|
| |
exception-definition |
|
| |
module module-name [ : module-type ] = module-expr |
|
| |
module type modtype-name = module-type |
|
| |
open module-path |
directive-argument |
::= |
nothing |
|
| |
string-literal |
|
| |
integer-literal |
|
| |
value-path |
コンパイルユニットの実装やモジュール式 struct ... end の中で書くように、定義の集合としてフレーズを書くことができます。定義は値の名前を束縛したり、型の名前を束縛したり、例外、モジュール名、モジュール型名を束縛したりできます。トップレベルシステムは束縛を行ったあと、その名前に定義したようなその型と値 (あればですが) を表示します。
フレーズには open 指示語 (セクション 6.11 を見てください) や値の式
(セクション 6.7 を見てください) もあります。式は評価するだけで、束縛はせずに評価した値を表示します。
最後に、フレーズには # (シャープ) で始まるトップレベル指示語もあります。この指示語はトップレベルシステムの動作をコントロールします。9.2 に指示語一覧があります。
Unix:
トップレベルシステムは以下のように ocaml コマンドで起動します。
ocaml options objects # interactive mode
ocaml options objects scriptfile # script mode
options は後述します。
objects は拡張子が .cmo or .cma のファイル名です。このファイルは options がセットされた直後、インタプリタにロードされます。scriptfile は拡張子が .cmo でも .cma でもない任意のファイル名です。
もしコマンドラインに scriptfile が与えられなかったら、トップレベルシステムは対話モードになります。対話モードでは入力は標準入力から読み込み、出力は標準出力に、エラーは標準エラー出力に書き出します。標準入力から End-of-file を受け取ったら ocaml は終了します (#quit を受け取っても終了します。セクション 9.2 を見てください) 。
起動時に (フレーズの読み込みを始める前に) 、カレントディレクトリに .ocamlinit があればそれを Objective Caml のフレーズ列として読み込み実行します (セクション 9.2 で解説されている #use 指示語で行うことと同じです) 。それぞれのフレーズは評価されるだけで出力は表示されません。
トップレベルシステムにはラインエディットはありませんが、fep のような外部コマンドラインエディタを使えば可能になります。fep -emacs ocaml や fep -vi ocaml などとするだけです。Gnu Emacs 上で ocaml を使用すれば、Emacs のエディット能力をすべて利用できます (Objective Caml の配布物にあるサブディレクトリ emacs を見てください) 。
フレーズの構文解析中でも、コンパイル中でも、評価中でも ctrl-C を押せば (正確には ocaml のプロセスに sigintr を送ることで) 中断することができます。トップレベルシステムはすぐに復帰して # プロンプトを表示します。
コマンドラインで ocaml に scriptfile が与えられた場合は、トップレベルシステムはスクリプトモードになります。ファイルから Objective Caml のフレーズ列を読み込み実行します (これもセクション 9.2 の #use 指示語と同じです) 。ocaml はファイル終端に至った時点で終了し、標準入力からコマンドを読み込むことはしません。Sys.argv には Objective Caml パラメータは渡されず、先頭の Sys.argv.(0) にはスクリプトファイル名が入ります。
スクリプトモードではスクリプトの先頭行に #! がある場合その行は無視します。つまりファイル先頭に #!/usr/local/bin/ocaml と書けば実行可能なスクリプトファイルを作ることが理論上可能です。そのスクリプトを実行すると自動的にトップレベルシステムを起動します。しかし、ocaml 自体は #! スクリプトで、Unix カーネルは普通入れ子になった #! は扱いません。
However, ocaml itself is a #! script on most installations
of Objective Caml, and Unix kernels usually do not handle nested #!
scripts.
Windows:
上記の Unix 版と同じように動作するテキストベースの ocaml.exe コマンドに加えて、ocamlwin.exe という名前のグラフィカルユーザインターフェイスを持つトップレベルが使用できます。Windows のファイルマネージャかプログラムマネージャから起動してください。
「端末」ウィンドウは 2 つのペインに分かれます。下のペインはフレーズを入力して編集するところで、上のペインは Objective Caml のトップレベルシステムが処理した入力フレーズのコピーや、トップレベルの出力を表示します。「リターン」キーを押すと下のペインに書かれている内容が Objective Caml トップレベルに送られます。「エンター」キーを押すと、入力ウィンドウの内容は送信せず改行のみを挿入します (これはメニュー項目の「Preference」で設定できます) 。
The ``Terminal'' windows is split in two panes. Phrases are entered
and edited in the bottom pane. The top pane displays a copy of the
input phrases as they are processed by the Objective Caml toplevel,
interspersed with the toplevel responses. The ``Return'' key sends the
contents of the bottom pane to the Objective Caml toplevel. The ``Enter''
key inserts a newline without sending the contents of the Input
window. (This can be configured with the ``Preferences'' menu item.)
入力ウィンドウは標準の Windows インターフェイスで、その内容はいつでも編集できます。過去に実行したフレーズの履歴は別ウィンドウに保持され表示されます。
アプリケーション Camlwin を終了するには、「File」メニューから「Quit」を選択するか、後述する quit 関数を使用してください。
フレーズの構文解析中でも、コンパイル中でも、評価中でも メニュー項目の「Interrupt Objective Caml」を選択すれば中断することができます。トップレベルシステムはすぐに復帰して # プロンプトを表示します。
ocaml コマンドに認識されるコマンドラインオプションは以下の通りです。
- -I directory
-
ソースファイルやコンパイル済みファイルを検索するディレクトリのリストに与えられたディレクトリを追加します。デフォルトでは、まずカレントディレクトリが検索され、それから標準ライブラリディレクトリが検索されます。-I で追加されたディレクトリはカレントディレクトリより後に、標準ライブラリディレクトリより前に、コマンドラインに与えられた順に検索されます。
+ で始まるディレクトリ名は、標準ライブラリディレクトリからの相対パスとみなされます。例えば、-I +labltk は標準ライブラリのサブディレクトリ labltk を検索パスに追加します。
#directory 指示語を使えば、トップレベルを起動してからディレクトリを検索パスに追加できます (セクション 9.2) 。
- -nolabels
-
型の中でオプションではないラベルを無視します。適用にラベルは使用できません。パラメータの順序が絶対になります。
- -principal
-
型チェックの際 information path をチェックします。すべての型が principal way で導出されているか確認します。-principal モードでコンパイルの通るプログラムは必ずデフォルトモード (equivalent type) でもコンパイルが通ります。
- -rectypes
-
型チェックの際、任意の再帰的な型を許します。デフォルトでは、オブジェクト型を経由する再帰による再帰的な型しかサポートされません。
- -unsafe
-
8 章にある ocamlc の同名のオプションを見てください。
配列や文字列アクセスのバウンダリチェックをオフにします (v.(i) や s.[i]) 。-unsafe でコンパイルされたプログラムはいくらか速くなりますが安全ではありません。プログラムが配列や文字列のバウンド外をアクセスした場合何が起こるかわかりません。
- -w warning-list
-
引数 warning-list を見て警告を有効/無効にします。
Unix:
以下の環境変数も参照されます。
-
LC_CTYPE
- iso_8859_1 がセットされていると、アクセント付き文字 (ISO Latin-1 文字集合のもの) の文字列や文字リテラルがそのまま表示されます。セットされていないと、10 進エスケープ文字列が表示されます (\ddd) 。
- TERM
- エラーメッセージを表示するとき、可能ならばトップレベルはエラーの発生した位置にアンダーライン表示を行いますが、可能かどうかは TERM を見て出力ターミナルの種類を知り端末データベースとあわせて判断します。
トップレベルの動作をコントロールする識別子一覧です。ファイルをメモリにロードしたり、プログラムの実行をトレースしたりします。
注意: 識別子はすべて # (シャープ) で始まります。この識別子の前の # は自分で入力してください。対話式ループが表示するプロンプトではありません。例えば #quit;; と打つとトップレベルループは終了しますが、quit;; と打つと ``unbound value quit'' とエラーが出ます。
- #quit;;
-
トップレベルのループを抜け、ocaml コマンドを終了します。
- #labels bool;;
-
引数が false だったら関数型のラベルを無視するようになります。true だったらデフォルト動作 (commuting style) に戻します。
- #warnings "warning-list";;
-
引数を見て警告を有効/無効にします。
- #directory "dir-name";;
-
ソースファイルやコンパイル済みファイルを検索するディレクトリのリストに与えられたディレクトリを追加します。
- #cd "dir-name";;
-
カレントディレクトリを変更します。
- #load "file-name";;
-
バッチコンパイラ ocamlc が生成したバイトコードオブジェクトファイル (.cmo) をメモリにロードします。
- #use "file-name";;
-
与えられたファイルからソースフレーズを読み込んでコンパイルして実行します。これは原文をそのまま取り込んで、標準入力から打ち込んだのと同じように処理されます。最初にエラーが発生した時点でファイルの読み込みは終了します。
- #install_printer printer-name;;
-
この識別子は printer-name という名前の関数を、その関数が引数にとる型の値を表示する関数として登録します。そういう値を表示するとき printer-name がトップレベルから呼ばれます。
表示の関数 printer-name は Format.formatter -> t -> unit という型を持ち、t が表示する値の型になります。この関数は Format ライブラリにある関数を使用して、指定されたフォーマットで型 t の値をテキスト表記して表示します。過去の互換性として、printer-name には t -> unit という型を持ち、標準的なフォーマットで表示を行う関数を登録することもできますが、この使用法は推奨されません (deprecated) 。
- #remove_printer printer-name;;
-
トップレベルのプリンタテーブルから与えられた名前の関数を取り除きます。
- #trace function-name;;
-
この指示語を実行したら、function-name という名前の関数の呼び出しをトレースします。呼び出しごとに引数と評価結果や関数を脱出した例外を表示します。例外はその関数自身から発生したものか、その関数がさらに呼び出した先の関数から発生したもののどちらかです。関数がカリー化されていたら、関数に渡されたようにそれぞれの引数が表示されます。
- #untrace function-name;;
-
指定された関数のトレースをやめます。
- #untrace_all;;
-
今までトレースしていたすべての関数のトレースをやめます。
- #print_depth n;;
-
値を表示する最大の深さを n に制限します。値のこの深さを超えた部分は ... のように省略表記されます。
- #print_length n;;
-
値のノードの表示する数を最大 n に制限します。値の残りの部分は ... のように省略表記されます。
9.3 |
The toplevel and the module system |
|
コンパイルユニットの中で定義されている識別子は、分割コンパイルと同じメカニズムで、トップレベルフレーズから参照できます。限定された名前 (Modulename.localname) を使うか、open コンストラクトを使った後、限定されていない名前を使うことです (セクション 6.3 を見てください) 。
しかし、他のコンパイルユニットを参照する前に、そのユニットの実装がメモリになければなりません。トップレベルシステムは起動時に標準ライブラリモジュールをすべてメモリにのせます。ユーザモジュールの実装は上記の #load 指示語を使ってロードできます。実装がメモリにないユニットを参照しようとすると ``Reference to undefined global `...' '' とエラーになります。
open mod としても、トップレベルは mod のコンパイル済みインターフェイス (.cmi) にアクセスするだけで、mod の実装はロードされず、かつ mod がロードされていなくても何のエラーも出ないことに注意してください。``reference to undefined global mod'' というエラーは mod を参照している値やモジュールの定義を実行しようとしたときだけ発生します。
このセクションではよく見るエラーメッセージについて解説します。
- Cannot find file filename
-
ファイルがカレントディレクトリからも、検索パスのディレクトリからも見つかりません。
filename が mod.cmi というフォーマットだったら、コンパイルユニット mod を参照したが、そのコンパイル済みインターフェイスが見つからなかったということです。対策として、まず mod.mli か mod.ml をコンパイルして、コンパイル済みインターフェイス mod.cmi を作成してください。
filename が mod.cmo というフォーマットだったら、#load でバイトコードオブジェクトファイルをロードしようとしたが、そのファイルがなかったということです。対策として、mod.ml をコンパイルしてください。
プログラムが複数のディレクトリにまたがる場合は、そのディレクトリを検索するように指定していないのかもしれません。対策として、#directory 指示語を使って検索パスにディレクトリを適切に追加してください。
- This expression has type t1, but is used with type t2
-
セクション 8.4 を見てください。
- Reference to undefined global mod
-
#load でモジュールの実装をメモリにロードし忘れています。上記のセクション 9.3 を見てください。
9.5 |
Building custom toplevel systems: ocamlmktop |
|
ocamlmktop コマンドは、起動時にロードするべきユーザコードを内部に保持している Objective Caml トップレベルを作成します。
ocamlmktop コマンドは .cmo や .cma ファイルを引数に取り、Objective Caml トップレベルの実装であるオブジェクトファイルとリンクします。普通の使い方は以下のようになります。
ocamlmktop -o mytoplevel foo.cmo bar.cmo gee.cmo
これで Objective Caml のトップレベルシステムと 3 つの .cmo ファイルのコードを持つバイトコードファイル mytoplevel が生成されます。トップレベルは直接起動できます。
./mytoplevel
これで通常のトップレベルループに入りますが、1 つだけ異なるのは foo.cmo 、bar.cmo 、gee.cmo のコードが、通常は以下のようにしてロードするところを、起動した時点ですでにメモリにロードされているということです。
#load "foo.cmo";;
#load "bar.cmo";;
#load "gee.cmo";;
ただし Foo 、Bar 、Gee モジュールは open されていません。
open Foo;;
なので望むなら上記のように open する必要はあります。
ocamlmktop が認識するコマンドラインオプションは以下の通りです。
- -cclib libname
-
カスタムランタイムモードのリンクじ、C リンカにオプションとして -llibname を渡します。8 章にある ocamlc の同名のオプションを見てください。
- -ccopt option
-
カスタムランタイムモードでのリンク時、C コンパイラやリンカに与えられたオプションを渡します。8 章にある ocamlc の同名のオプションを見てください。
- -custom
-
カスタムランタイムモードでリンクします。8 章にある ocamlc の同名のオプションを見てください。
- -I directory
-
コンパイル済みオブジェクトコードファイル (.cmo や .cma) を検索するディレクトリのリストに与えられたディレクトリを追加します。
- -o exec-file
-
リンカが出力するトップレベルファイルの名前を指定します。デフォルトでは a.out です。