いつ使用するか教えていただけthrow
ますexit
かerror
?
1> catch throw ({aaa}).
{aaa}
2> catch exit ({aaa}).
{'EXIT',{aaa}}
3> catch gen_server:call(aaa,{aaa}).
{'EXIT',{noproc,{gen_server,call,[aaa,{aaa}]}}}
4> catch exit("jaj")
{'EXIT',"jaj"}
いつ使用するか教えていただけthrow
ますexit
かerror
?
1> catch throw ({aaa}).
{aaa}
2> catch exit ({aaa}).
{'EXIT',{aaa}}
3> catch gen_server:call(aaa,{aaa}).
{'EXIT',{noproc,{gen_server,call,[aaa,{aaa}]}}}
4> catch exit("jaj")
{'EXIT',"jaj"}
:、、でキャッチできるクラスは3つあります。try ... catch
throw
error
exit
throw
を使用して生成され、非ローカルリターンthrow/1
に使用することを目的としており、キャッチされない限り(エラーが発生した場合)、エラーは生成されません。nocatch
error
システムがエラーを検出すると生成されます。を使用して明示的にエラーを生成できますerror/1
。システムは、生成されたエラー値にスタックトレースも含めます。たとえば、{badarg,[...]}
。
exit
を使用して生成さexit/1
れ、このプロセスが終了することを通知することを目的としています。
error/1
との違いexit/1
はそれほど大きくはありません。エラーによって生成されたスタックトレースが強化する意図についてです。
それらの違いは、実際には次のcatch ...
場合により顕著になります。throw/1
を使用するとcatch
、非ローカルリターンから予想されるように、がスローされた値を返すだけです。anerror/1
を使用すると、スタックトレースが含まれる場所がcatch
返されます。from fromも戻りますが、実際の終了理由のみが含まれます。それはそれらを同一視しているように見えますが、それらは/非常に異なっていました。{'EXIT',Reason}
Reason
exit/1
catch
{'EXIT',Reason}
Reason
try ... catch
[更新しました]
私は、Robert Virdingが指摘した、スローとエラーの重要な違いについて詳しく説明しました。この編集は記録のためだけです!
throw は、他の言語でerror
使用する場所で使用されます。throw
実行中のプロセスのエラーがコードによって検出されました。これは、。で例外を通知しますerror/1
。同じプロセスがそれをキャッチし(おそらくスタックの上位)、エラーは同じプロセス内で処理されます。error
常にスタックトレースをもたらします。
throw
エラーを通知するためではなく、深くネストされた関数から値を返すために使用されます。スタックを巻き戻すので、呼び出しthrow
はスローされた値をキャッチされた場所に返します。の場合と同様に、error
スローされたものをキャッチしています。スローされたものだけがエラーではなく、スタックに渡された値だけでした。これが、throwがスタックトレースをもたらさない理由です。
exists
不自然な例として、リストの関数を実装したい場合(実行するのと同様list:any
)、自分で再帰を行わずに演習として、を使用してlist:foreach
、throw
ここで使用できます。
exists(P, List) ->
F = fun(X) ->
case P(X) of
true -> throw(true);
Whatever -> Whatever
end
end,
try lists:foreach(F, List) of
ok -> false
catch
true -> true
end.
error
スローされたがキャッチされなかった値は、 :として扱われnocatch
ます。例外が生成されます。
EXITは、プロセスが「あきらめる」ときに通知されます。親プロセスはEXITを処理しますが、子プロセスは終了します。これがErlangのlet-it-crash哲学です。
したがって、exit/1
のEXITは同じプロセス内でキャッチされるのではなく、親に任されます。error/1
のエラーはプロセスにローカルです。つまり、何が発生し、プロセス自体によってどのように処理されるかが問題になります。throw/1
スタック全体の制御フローに使用されます。
[アップデート]
exit/2
-で呼び出される-もあることに注意してください。親プロセスを意味します。Pid
exit/1
私はErlangを初めて使用しますが、これらが何であるか、それらの違い、それらが何に使用されるかなどについて、次のように考えます。
throw
:ローカルで(つまり、現在のプロセス内で)処理する必要がある条件。たとえば、呼び出し元はコレクション内の要素を探していますが、コレクションにそのような要素が実際に含まれているかどうかはわかりません。次に、そのような要素が存在しない場合、呼び出し先はスローする可能性があり、呼び出し元はを使用して不在を検出しtry[/of]/catch
ます。発信者がこれを怠ると、これはnocatch
error
(以下で説明する)に変わります。
exit
:現在のプロセスが完了しました。たとえば、単に終了した(この場合、normal
元の関数が戻ってきたのと同じように扱われる)を渡すか、操作がキャンセルされました(たとえば、通常は無期限にループしますが、shut_down
メッセージを受信したばかりです)。
error
:プロセスが何かを実行した、および/またはプログラマーが考慮しなかった状態に達した(例:1/0)、不可能であると信じている(例case ... of
、どのケースにも一致しない値に遭遇した)、または何らかの前提条件が満たされていない(例:たとえば、入力は空ではありません)。この場合、ローカルリカバリは意味がありません。したがって、どちらthrow
もexit
適切ではありません。これは予期しないことなので、スタックトレースはReasonの一部です。
ご覧のとおり、上記のリストはエスカレーション順になっています。
throw
発信者が処理することが期待される正常な状態のためのものです。つまり、処理は現在のプロセス内で行われます。
exit
も正気ですが、プロセスが完了したという理由だけで現在のプロセスを終了する必要があります。
error
非常識です。合理的に回復できない何かが発生し(通常はバグ?)、ローカル回復は適切ではありません。
対他の言語:
throw
チェックされた例外がJavaで使用される方法に類似しています。一方、error
は、チェックされていない例外により類似した方法で使用されます。チェックされた例外は、呼び出し元に処理させたい例外です。Javaでは、呼び出しをラップするか、メソッドでそのような例外try/catch
を宣言する必要があります。throws
一方、チェックされていない例外は通常、最も外側の呼び出し元に伝播します。
exit
Java、C ++、Python、JavaScript、Rubyなどのより「従来の」言語での優れたアナログはありません。exit
漠然とuber-のようにreturn
:最後に戻る代わりに、関数の途中から戻ることができます。現在の関数から戻るだけでなく、すべてから戻ります。
exit
例
serve_good_times() ->
receive
{top_of_the_mornin, Sender} ->
Sender ! and_the_rest_of_the_day_to_yourself;
{you_suck, Sender} ->
Sender ! take_a_chill_pill;
% More cases...
shut_down ->
exit(normal)
end,
serve_good_times()
end
ほとんどすべてのメッセージの後で自分自身を呼び出すのでserve_good_times
、プログラマーは、すべての受信ケースでその呼び出しを繰り返したくないと判断しました。したがって、彼女は受信後にその呼び出しを行いました。しかし、それでは、自分serve_good_times
自身を呼び出すのをやめることにした場合はどうなるでしょうか。これがexit
救いの手を差し伸べるところです。normal
toを渡すとexit
、最後の関数呼び出しが戻ったかのようにプロセスが終了します。
exit
そのため、のような汎用ライブラリを呼び出すことは一般的に不適切lists
です。プロセスを終了するかどうかは、図書館の仕事ではありません。これは、アプリケーションコードによって決定する必要があります。
異常はexit
どうですか?
exit
これは、別のプロセス(「リモート」プロセス)が、呼び出す(呼び出されなかった) 「ローカル」プロセスにリンクされている場合に重要ですprocess_flag(trap_exit, true)
。最後に戻った関数と同様に、exit(normal)
リモートプロセスを終了させません。ただし、ローカルプロセスがexit(herp_derp)
呼び出しを行うと、リモートプロセスも。で終了しReason=herp_derp
ます。もちろん、リモートプロセスがさらに多くのプロセスにリンクされている場合、それらは。で終了信号も受け取りますReason=herp_derp
。したがって、非normal
出口は連鎖反応を引き起こします。
これを実際に見てみましょう:
1> self().
<0.32.0>
2> spawn_link(fun() -> exit(normal) end).
<0.35.0>
3> self().
<0.32.0>
4>
4>
4> spawn_link(fun() -> exit(abnormal) end).
** exception exit: abnormal
5> self().
<0.39.0>
6>
スポーンした最初のプロセスでは、シェルは終了しませんでした(self
前後で同じpidが返されたため、わかりますspawn_link
)。しかし、2番目のプロセスによってシェルが終了しました(システムはシェルプロセスを新しいものに置き換えました)。
もちろん、リモートプロセスが使用する場合は、ローカルプロセスがに渡されるか、他の何かがにprocess_flag(trap_exit, true)
渡されるかに関係なく、メッセージを受け取るだけです。このフラグを設定すると、連鎖反応が停止します。normal
exit
6> process_flag(trap_exit, true).
false
7> spawn_link(fun() -> exit(normal) end).
<0.43.0>
8> self().
<0.39.0>
9> flush().
Shell got {'EXIT',<0.43.0>,normal}
ok
10>
10>
10> spawn_link(fun() -> exit(abnormal) end).
<0.47.0>
11> self().
<0.39.0>
12> flush().
Shell got {'EXIT',<0.47.0>,abnormal}
exit(normal)
これは元の関数が返すように扱われると言ったことを思い出してください。
13> spawn_link(fun() -> ok end).
<0.51.0>
14> flush().
Shell got {'EXIT',<0.51.0>,normal}
ok
15> self().
<0.39.0>
あなたは何を知っていますか:呼び出されたときと同じことが起こりましたexit(normal)
。素晴らしい!