はじめてなElixir(12)でうまくいっていなさそうなところを説明する

[はじめてなElixir(12)いろんな方法で並行プロセスを作る（失敗編）](https://qiita.com/kikuyuta/items/f861379706361beacc2c) が、やったこととうまくいっていないところも書いてくれているので手助けしやすい気がする。応援の気持ちをこめ、やりたいことができるように書いてみます。

# プロセスの終了を捕まえる

[spawn_link でプロセスの終了を捕まえる](https://qiita.com/kikuyuta/items/f861379706361beacc2c#spawn_link-%E3%81%A7%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%AE%E7%B5%82%E4%BA%86%E3%82%92%E6%8D%95%E3%81%BE%E3%81%88%E3%82%8B)で、何の反応もなくプロセスが終了しているように見えてしまいます。実はメッセージにプロセス終了のお知らせがきているのでした。

`Process.flag(:trap_exit, true)` を宣言したプロセスは、そのプロセスから spawn_link した先のプロセスが異常終了したときに **メッセージ** を受けとります。ですから `receive do mes` の `mes` を表示してみると内容が表示されているはずです。

メッセージの中身を表示するようにした差分です

```diff
$ diff -w link10.orig.ex link10.ex
17c17
<     receive do mes -> mes
---
>     receive do mes -> IO.inspect({:test_next_temp, mes})
25c25
<     receive do mes -> mes end
---
>     receive do mes -> IO.inspect({:test_sensor, mes}) end
```

コードです

```elixir
defmodule Link10 do
  def pseudo_temp_sensor(temp) do
    receive do
      {sender, {:init, init_temp}} ->
        send(sender, init_temp)
        pseudo_temp_sensor(init_temp)
      {sender, :next} ->
        if(Enum.random(0..9) == 0, do: exit(:boom))
        send(sender, temp)
        cur = temp + Enum.random(0..3) * Enum.random(-1..1)
        pseudo_temp_sensor(cur)
    end
  end

  def test_next_temp(pid) do
    send(pid, {self(), :next})
    receive do mes -> IO.inspect({:test_next_temp, mes})
    end
  end

  def test_sensor(zero_th, num) do
    Process.flag(:trap_exit, true) # これを追加
    pid = spawn_link(Link10, :pseudo_temp_sensor, [nil])
    send(pid, {self(), {:init, zero_th}})
    receive do mes -> IO.inspect({:test_sensor, mes}) end
    1..num |> Enum.map(fn(_void) -> test_next_temp(pid) end)
  end
end
```

これを動かしてみると

```elixir
$ iex
Erlang/OTP 21 [erts-10.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c "link10.ex"
[Link10]
iex(2)> Link10.test_sensor(0,10)
{:test_sensor, 0}
{:test_next_temp, 0}
{:test_next_temp, 0}
{:test_next_temp, -3}
{:test_next_temp, -1}
{:test_next_temp, -1}
{:test_next_temp, -1}
{:test_next_temp, -1}
{:test_next_temp, -3}
{:test_next_temp, {:EXIT, #PID<0.113.0>, :boom}}
```

`{:EXIT, #PID<0.113.0>, :boom}` というメッセージが取得できていることがわかりますね。

# spawn_monitor 

[さらに別のプロセスの立て方を試す (monitor)](https://qiita.com/kikuyuta/items/f861379706361beacc2c#%E3%81%95%E3%82%89%E3%81%AB%E5%88%A5%E3%81%AE%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%AE%E7%AB%8B%E3%81%A6%E6%96%B9%E3%82%92%E8%A9%A6%E3%81%99-monitor) でうまくいかないのは、 `spawn` と `spawn_monitor` で返り値が異なるためです。

spawn は `pid` が返ります。
spawn_monitor は二要素のタプル(`{x, y}` で表わされている)が返ります。ですから pid を使いたいなら `{pid, ref} = spawn_monitor...` で受けとるといいです。

```elixir
$ iex
Erlang/OTP 21 [erts-10.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe] [dtrace]

Interactive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> spawn fn -> nil end
#PID<0.107.0>
iex(2)> spawn_monitor fn -> nil end
{#PID<0.109.0>, #Reference<0.2955252636.2417754114.137078>}
iex(3)> {pid, ref} = spawn_monitor fn -> nil end
{#PID<0.111.0>, #Reference<0.2955252636.2417754114.137093>}
iex(4)> pid
#PID<0.111.0>
iex(5)> ref
#Reference<0.2955252636.2417754114.137093>
```

元記事のコードだと
`send(pid, {self(), {:init, zero_th}})` が
`send({#PID<0.111.0>, #Reference<0.2955252636.2417754114.137093>}, {self(), {:init, zero_th}})` という形での呼び出しに相当してしまうのでうまくいかなかったのですね。

----

こんな感じです。応援してます。