Erlang プロセスの終わらせ方と error/1, throw/1, exit/1

## この資料は
`error/1`, `throw/1`, `exit/1` の使い分けについて．
gg っても日本語のいいまとめが出てこないので，じゃあ書こうじゃないかと．
正確にまとめられてるかわからないですけど．

## とりあえず catch してみましょうぞ
`error/1`, `throw/1`, `exit/1` をそれぞれ `catch` した．

    1> catch error(hoge).
    {'EXIT',{hoge,[{erl_eval,do_apply,6,
                         [{file,"erl_eval.erl"},{line,673}]},
               {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,431}]},
               {shell,exprs,7,[{file,"shell.erl"},{line,686}]},
               {shell,eval_exprs,7,[{file,"shell.erl"},{line,641}]},
               {shell,eval_loop,3,[{file,"shell.erl"},{line,626}]}]}}
    2> catch throw(hoge).
    hoge
    3> catch exit(hoge).
    {'EXIT',hoge}

これから推測されることは以下．

 - `error/1` と `exit/1` はプロセスを殺す力を持っている(`DOWN`)が， `throw/1` は持っていない．
 - `throw/1` は大域脱出でしかない．プロセスを殺す力を持っておらず，投げられた例外は同一プロセス内でなんとかしなければならない．
 - `exit/1` ではスタックトレースはつかないのは， `exit/1` が正常系のプロセス死にも利用されるから．


## 使い分け

`catch` した結果をふまえると， この 3 つの関数の使い分けは以下の感じでいいだろう．

### exit/1
文字通りプロセスを終了させたい場合に用いる．
単に役目が終わったプロセスを明示的に殺すだけなので，エラーではないのだね．

    Eshell V7.0  (abort with ^G)
    1> spawn(fun() -> exit(hogehoge) end).
    <0.35.0>
    2> 

monitor すればプロセスが死んだ Reason がとれる．

    Eshell V7.0  (abort with ^G)
    1> spawn_monitor(fun() -> exit(hogehoge) end).
    {<0.35.0>,#Ref<0.0.4.29>}
    2> flush().
    Shell got {'DOWN',#Ref<0.0.4.29>,process,<0.35.0>,hogehoge}
    ok

### throw/1
なにかエラーが発生したわけではない．ネストした関数呼び出しから一気に脱出 (大域脱出)するために用いる．
プロセス内でハンドリングされることを想定しているため，その `try ... catch` がないとエラーとなりプロセスが死ぬ．

    Eshell V7.0  (abort with ^G)
    1> spawn(fun() -> throw(hogehoge) end).
    <0.35.0>
    
    =ERROR REPORT==== 24-Oct-2016::18:58:27 ===
    Error in process <0.35.0> with exit value:
    {{nocatch,hogehoge},[{erlang,apply,2,[]}]}

monitor してみると，ブロセスが死んだ直接的な原因は， `hogehoge` が throw されたからではなくて， throw された `hogehoge` が catch されなかったから，ということになっている．

    1> spawn_monitor(fun() -> throw(hogehoge) end).
    
    =ERROR REPORT==== 24-Oct-2016::18:59:41 ===
    Error in process <0.37.0> with exit value:
    {{nocatch,hogehoge},[{erlang,apply,2,[]}]}
    {<0.37.0>,#Ref<0.0.4.34>}
    2> flush().
    Shell got {'DOWN',#Ref<0.0.4.34>,process,<0.37.0>,
                  {{nocatch,hogehoge},[{erlang,apply,2,[]}]}}

つまりプロセスを殺すことが目的だったら， `throw/1` は不適切．

### error/1
何か問題が発生した． `badmatch` とか `case_clause` は Erlang VM が吐いてくれるエラーで，それらと同じレベルで，なにかユーザ定義の「問題」が発生したよ，というのが error．
プロセスは続行できなくなり死ぬ．

    4> spawn(fun() -> error(hogehoge) end).
    
    =ERROR REPORT==== 24-Oct-2016::19:04:01 ===
    Error in process <0.40.0> with exit value:
    {hogehoge,[{erlang,apply,2,[]}]}
    <0.40.0>

monitor してみると，見た目は throw と一緒． throw でプロセスが死ぬのは， `nocatch` エラーだから．
ただし， `error/1` の引数で指定した Reason (`hogehoge`) がそのままエラーの原因になっている (nocatch はつかない)．

    5> spawn_monitor(fun() -> error(hogehoge) end).
    {<0.42.0>,#Ref<0.0.4.51>}

    =ERROR REPORT==== 24-Oct-2016::19:04:14 ===
    Error in process <0.42.0> with exit value:
    {hogehoge,[{erlang,apply,2,[]}]}
    6> flush().
    Shell got {'DOWN',#Ref<0.0.4.51>,process,<0.42.0>,
                  {hogehoge,[{erlang,apply,2,[]}]}}


## まとめ
プロセスが死ぬ原因は 2 つ．

 1. `exit/1` により明示的に殺した(死んだ)
 2. エラーが発生し，実行が続行できなくなった

それぞれをユーザが明示的にコーディングする方法(として適切なの)は，それぞれ以下．

 1. `exit/1` で明示的に殺す．
 2. `error/1` で明示的にエラーを発生させる．

つまり， error/1, throw/1, exit/1 の使い分けは，簡単に書けば以下．

 - `error/1`: プロセスの実行が続行できなくなるようなエラーが発生した
 - `exit/1`: プロセスは役割を果たしたので死ぬ (もしくは死ぬことで役割を果たす)
 - `throw/1`: 大域脱出