Erlangの例外は、単にメッセージが謎めいているだけではなくて、他のプログラミング言語とはだいぶ趣<おもむき>が異なるので、理解して正しく使うのはなかなか大変です。そのことはまたの機会に、ということで、このエントリーでは、キャッチされなかった例外がどうなるかを書いておきます。
Erlangのcatchオペレータ(言語構文に含まれる特殊形式だけど、「オペレータ」と呼ぶみたい)は、例外を補足捕捉して通常の値に変換します。Erlang/OTPリリース10からは、catchオペレータよりずっと洗練されたtry構文があります。これらの例外補足捕捉機構でハンドルされなかった例外は、(Erlang内の)プロセスを終了させます。
プロセスが終了すると、そのプロセスとリンクされているすべてのプロセスに終了シグナルが送られます。終了シグナルに乗って運ばれるデータを終了ターム(exit term)と呼ぶのですが、例外の種類*1ごとに終了タームがどうなるかまとめてみました。
例外の生成 | catchによる値 | 終了ターム |
---|---|---|
throw(Term) | Term | {{nocatch, Term}, Stack} |
exit(Term) | {'EXIT' Term} | Term |
erlang:error(Term) | {'EXIT' {Term, Stack}} | {Term, Stack} |
Stackはシンボリックなスタックトレースです(「分かりにくいと評判のErlangエラーのまとめ」参照)。error例外は、多くの場合、プログラマが erlang:error/1 を呼ぶのではなくてランタイムシステムが発行します。
次は実験用のサンプルです。
%% exit_test.erl-module(exit_test).
-compile(export_all).start() ->
spawn(fun parent_main/0).parent_main() ->
process_flag(trap_exit, true),
Pid = spawn_link(fun child_loop/0),
parent_loop(Pid).parent_loop(Pid) ->
receive
stop ->
io:fwrite("Parent: byebye.~n"),
exit(normal);
{'EXIT', Pid, Why} ->
io:fwrite("Parent: child exited. :~p~n", [Why]),
NewPid = spawn_link(fun child_loop/0),
parent_loop(NewPid);
Message ->
Pid ! Message, % forward to the child
parent_loop(Pid)
end.child_loop() ->
receive
{error, Term} ->
io:fwrite("Child: error received.~n"),
erlang:error(Term),
child_loop();
{exit, Term} ->
io:fwrite("Child: exit received.~n"),
exit(Term),
child_loop();
{throw, Term} ->
io:fwrite("Child: throw received.~n"),
throw(Term),
child_loop();
Other ->
io:fwrite("Child: unknown message:~p~n", [Other]),
child_loop()
end.