Erlangのエラーメッセージは暗号のようだ(Cryptic error messages)とか言われたりします。確かに分かりにくいです。http://bluebones.net/2006/12/erlang-error-messages/ に良いまとめがあった*1ので、これを敷衍<ふえん>する形で説明します。
内容:
- 例外の一般論
- ランタイムエラーとスタックトレース
- reasonタームの一覧
●例外の一般論
Erlangの例外(exceptions)は次の3種に分類されます。
それぞれの例外を、次の関数で引き起こす(raiseする)ことができます。
(catch 式).
を評価すれば、式で生成される例外データを見ることができます。
1> (catch throw(foo)).
foo
2> (catch exit(foo)).
{'EXIT',foo}
3> (catch erlang:error(foo)).
{'EXIT',{foo,[{erl_eval,do_apply,5},
{erl_eval,expr,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
4>
throw例外とexit例外の発生はユーザー(プログラマ)が制御できますが、error例外はランタイムシステムが生成することがほとんどです。ランタイムシステムが検出したエラーに関しては、決まったフォーマットのデータが投げられます -- これが分かりにくいと評判なんですね。
●ランタイムエラーとスタックトレース
ランタイムシステムが投げるエラーデータ(Erlangターム)は次の形をしています。
- {'EXIT', {Why, Where}}
Whyはreasonタームと呼ばれる部分で、次の節に、一覧表にまとめてあります。Whereはシンボリックに表現されたスタックトレースです。次の実例を見てみましょう。
4> (catch 1 / 0).
{'EXIT',{badarith,[{erlang,'/',[1,0]},
{erl_eval,do_apply,5},
{erl_eval,expr,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
5>
スタックトレースの部分だけを取り出すと:
[
{erlang,'/',[1,0]}, % (1) erlang:'/'(1, 0)
{erl_eval,do_apply,5}, % (2) erl_eval:do_apply/5
{erl_eval,expr,5}, % (3) erl_eval:expr/5
{shell,exprs,6}, % (4) shell:exprs/6
{shell,eval_exprs,6}, % (5) shell:eval_exprs/6
{shell,eval_loop,3} % (6) shell:eval_loop/3
]
次のことに注意すれば読み解くことができます。
- 各項目は {モジュール, 関数名, 引数} の形。
- 引数のところが整数値なら、それは引数個数(アリティ)を表す。
- 引数のところがリストなら、渡された引数そのものを表す。
- 最初の項目が例外が起きた関数を示す。
- スタック全体が含まれるとは限らない。どこまで(何個分だけ)トレースするかは実装依存。
●reasonタームの一覧
一覧表の例に書いてある式 を (catch 式).
で実行すると当該ランタイムエラーが発生します。簡単に引き起こせないエラーの例は書いてありません。
理由 | 説明 | 例 |
---|---|---|
badarg | 引数の型が不正。 | io:fwrite(foo, bar) |
badarith | 算術演算の引数が不正。 | 1 / 0 |
{badmatch,V} | パターンマッチが失敗。値Vがマッチしない。 | a = b |
function_clause | 関数呼び出しにおいて、マッチする関数節が見つからない。 | io:fwrite(1, 2, 3) |
{case_clause,V} | case式において、マッチする分岐が見つからない。値Vがマッチしない。 | case 1 of 2->ok end |
if_clause | if式において、真となる分岐が見つからない。 | if false->ok end |
{try_clause,V} | try式のof節において、マッチする分岐が見つからない。値Vがマッチしない。 | try a of b -> ok catch _ -> ng end |
undef | 関数呼び出しにおいて、関数が見つからない。 | not_defined() |
{badfun,F} | 関数Fに関してなにか不具合がある。 | - |
{badarity,F} | fun式に対する引数の個数が食い違っている。Fはfun式の実体と引数を記述するタプル。 | (fun(X)->X end)(1, 2) |
timeout_value | receive after式のタイムアウト値が、整数でもinfinityでもない。 | receive after a -> ok end |
noproc | 存在しないプロセスにリンクしようとした。 | link(pid(0, 123, 456)) |
{nocatch,V} | catchの外でthrow式を評価しようとした。Vはthrowしたターム。 | spawn(fun ()->throw(foo) end) |
system_limit | システムリミットに到達した。システムリミットに関する情報は「Efficiency Guide」を参照。 | - |
*1:[追記]同じ内容は、http://erlang.org/doc/reference_manual/errors.html にありました。こっちがオリジナルで、bluebones.netブログのほうは単にコピーでした。[/追記]