Chapter 9 トップレベル対話環境 (ocaml)

このページは最後に更新されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

last mod. 2008-10-01 (水) 11:35:58

The Objective Caml system release 3.10

この章では Objective Caml のトップレベルシステムについて解説します。トップレベルシステムとは Objective Caml システムを read-eval-print のループを通して対話的に利用できるシステムです。このモードでは、システムは入力から Caml のフレーズを読み、型チェックし、コンパイルし評価して、導出された型と結果の値 (あればですが) を表示します。システムではフレーズを読む前に # (シャープ) プロンプトを表示します。

トップレベルに複数行にまたがった入力を与えることも可能です。入力は ;; (2 つのセミコロン) で終わります。トップレベルの入力には以下の文法で複数のトップレベルフレーズを与えることができます。

toplevel-input	::=	{ toplevel-phrase } ;;  

toplevel-phrase ::=	definition  
	| expr  
	∣ # ident  directive-argument  

directive-argument ::=	є  
	| 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か.cma のファイル名です。このファイルは options がセットされた直後、インタプリタにロードされます。scriptfileは拡張子が .cmo でも .cma でもない任意のファイル名です。

もしコマンドラインにscriptfileが与えられなかったら、トップレベルシステムは対話モードになります。対話モードでは入力は標準入力から読み込み、出力は標準出力に、エラーは標準エラー出力に書き出します。標準入力から End-of-file を受け取ったら ocaml は終了します (#quit を受け取っても終了します。セクション 9.2 を見てください) 。

起動時に (フレーズの読み込みを始める前に) 、カレントディレクトリに .ocamlinit があればそれを Objective Caml のフレーズ列として読み込み実行します (セクション 9.2 で解説されている #use 指示語で行うことと同じです) 。それぞれのフレーズは評価されるだけで出力は表示されません。カレントディレクトリに.ocamlinitがない場合は、 ユーザのホームディレクトリ(環境変数HOME)から読み込まれ、上記のように実行されます。

トップレベルシステムにはラインエディットはありませんが、leditやocaml2、rlwrapのような外部コマンドラインエディタ(the Caml Humpを参照)を使えば可能になります。また、Gnu Emacs 上で ocaml を使用すれば、Emacs のエディット能力をすべて利用できます (inf-caml内のrun-camlコマンド) 。

フレーズの構文解析中でも、コンパイル中でも、評価中でも ctrl-C を押せば (正確には ocaml のプロセスに INTRシグナル を送ることで) 中断することができます。トップレベルシステムはすぐに復帰して # プロンプトを表示します。

コマンドラインで ocaml にscriptfileが与えられた場合は、トップレベルシステムはスクリプトモードになります。ファイルから Objective Caml のフレーズ列を読み込み実行します (これもセクション 9.2 の #use 指示語と同じです) 。ocaml はファイル終端に至った時点で終了し、標準入力からコマンドを読み込むことはしません。Sys.argv には Objective Caml パラメータは渡されず、先頭の Sys.argv.(0) にはスクリプトファイル名が入ります。

スクリプトモードではスクリプトの先頭行に #! がある場合その行は無視します。つまりファイル先頭に #!/usr/local/bin/ocaml と書けば実行可能なスクリプトファイルを作ることが理論上可能です。そのスクリプトを実行すると自動的にトップレベルシステムを起動します。しかし、ocaml 自体は #! スクリプトで、Unix カーネルは普通入れ子になった #! は扱いません。最初の一行を以下のようにすることで、この問題を解決できます。

#!/usr/local/bin/ocamlrun /usr/local/bin/ocaml

Windows:

上記の Unix 版と同じように動作するテキストベースの ocaml.exe コマンドに加えて、ocamlwin.exe という名前のグラフィカルユーザインターフェイスを持つトップレベルが使用できます。Windows のファイルマネージャかプログラムマネージャから起動してください。

9.1 Options

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 でコンパイルされたプログラムはいくらか速くなりますが安全ではありません。プログラムが配列や文字列のバウンド外をアクセスした場合何が起こるかわかりません。
-version
バージョンを表示し、終了します。
-w warning-list
引数warning-listを見て警告を有効/無効にします。

Unix:

以下の環境変数も参照されます。

LC_CTYPE
iso_8859_1 がセットされていると、アクセント付き文字 (ISO Latin-1 文字集合のもの) の文字列や文字リテラルがそのまま表示されます。セットされていないと、10 進エスケープ文字列が表示されます (\ddd) 。
TERM
エラーメッセージを表示するとき、可能ならばトップレベルはエラーの発生した位置にアンダーライン表示を行いますが、可能かどうかは TERM を見て出力ターミナルの種類を知り端末データベースとあわせて判断します。
HOME
.ocamlinitファイルが検索されるディレクトリ

9.2 Toplevel directives

トップレベルの動作をコントロールする識別子一覧です。ファイルをメモリにロードしたり、プログラムの実行をトレースしたりします。

注意: 識別子はすべて # (シャープ) で始まります。この識別子の前の # は自分で入力してください。対話式ループが表示するプロンプトではありません。例えば #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) をメモリにロードします。(訳注: .cmaも可能)
#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 を参照している値やモジュールの定義を実行しようとしたときだけ発生します。

9.4 Common errors

このセクションではよく見るエラーメッセージについて解説します。

Cannot find file filename
ファイルがカレントディレクトリからも、検索パスのディレクトリからも見つかりません。
filenamemod.cmiというフォーマットだったら、コンパイルユニットmodを参照したが、そのコンパイル済みインターフェイスが見つからなかったということです。対策として、まずmod.mlimod.mlをコンパイルして、コンパイル済みインターフェイスmod.cmiを作成してください。
filenamemod.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 する必要はあります。

9.6 Options

The following command-line options are recognized by ocamlmktop.

-cclib libname
カスタムランタイムモードのリンクじ、C リンカにオプションとして -llibname を渡します。8 章にある ocamlc の同名のオプションを見てください。
-ccopt option
カスタムランタイムモードでのリンク時、C コンパイラやリンカに与えられたオプションを渡します。8 章にある ocamlc の同名のオプションを見てください。
-custom
カスタムランタイムモードでリンクします。8 章にある ocamlc の同名のオプションを見てください。
-I directory
コンパイル済みオブジェクトコードファイル (.cmo や .cma) を検索するディレクトリのリストに与えられたディレクトリを追加します。
-o exec-file
リンカが出力するトップレベルファイルの名前を指定します。デフォルトでは a.out です。

新規 編集 添付