Elixirチュートリアル プロセス

## 11.1 spawn(生み出す) - spawn

spawn でプロセスを生み出せる。
実行するとPIDがかえってくる。

```ex
iex> spawn fn -> 1 + 2 end
#PID<0.68.0>
```

プロセスが生きてるかどうかは Proscess.alive?/1 で確認できる。
生み出したプロセスはすぐ死ぬ。

```ex
iex> pid = spawn fn -> 1 + 2 end
#PID<0.64.0>
iex> Process.alive?(pid)
false
```

self/0 で今現在生きているプロセスの PID を取得できる

```ex
iex> self
#PID<0.57.0>
iex> Process.alive? self
true # 生きてる生きてる
```

## 11.2 送信と受信 - send and receive

自身のプロセスに処理を投げてみる。

```ex
iex> send self, {:hello, "world"}
{:hello, "world"}
iex> receive do
...>   {:hello, msg} -> msg
...>   {:world, msg} -> "won't match"
...> end
"world"

# send self, {:hello, "world"} がメールボックスに入っていたので処理してる

iex> send self, {:world, "world"}
{:world, "world"}
iex> receive do
...>   {:world, msg} -> "won't match"
...> end
"won't match"

# send self, {:world, "world"} がメールボックスに入っていたので処理してる

iex> send self, {:world, "world"}
{:world, "world"}
iex> send self, {:hello, "world"}
{:hello, "world"}
iex> receive do
...>   {:hello, msg} -> msg
...>   {:world, msg} -> "won't match"
...> end
"won't match"

# 最初に突っ込まれてるキューが実行されて {:world, msg} がパターンマッチして "won't match" が出力されている

iex> receive do
...>   {:hello, msg} -> msg
...>   {:world, msg} -> "won't match"
...> end
"world"

# 二番目に突っ込まれてるキューが実行される

iex> receive do
...>   {:hello, msg} -> msg
...>   {:world, msg} -> "won't match"
...> end

# メッセージ待ち
```

after でタイムアウトも指定できる
以下は 1秒待ってタイムアウトする場合

```ex
receive do
  {:hello, msg}  -> msg
after
  1_000 -> "nothing after 1s"
end
```

プロセス立ち上げて自身のプロセスに投げて処理させる。

```ex
iex> parent = self()
#PID<0.57.0>
iex> spawn fn -> send(parent, {:hello, self()}) end
#PID<0.85.0>
iex> spawn fn -> send(parent, {:hello, self()}) end
#PID<0.87.0>

iex> receive do
...>   {:hello, pid} -> "Got hello from #{inspect pid}"
...> end
"Got hello from #PID<0.85.0>"
iex> receive do
...>   {:hello, pid} -> "Got hello from #{inspect pid}"
...> end
"Got hello from #PID<0.87.0>"
```

flush でスタックされてるプロセスを flush できる。

```ex
iex> spawn fn -> send(parent, {:hello, self()}) end
#PID<0.89.0>
iex> spawn fn -> send(parent, {:hello, self()}) end
#PID<0.91.0>
iex> spawn fn -> send(parent, {:hello, self()}) end
#PID<0.93.0>
iex(31)> flush
{:hello, #PID<0.89.0>}
{:hello, #PID<0.91.0>}
{:hello, #PID<0.93.0>}
:ok

iex> flush
:ok
```

## 11.3 リンク - Links

普通に spawn して失敗した時は、プロセス同士は独立しているため、プロセス内でエラーを吐くだけで、親プロセスには失敗が伝搬されない。

```ex
iex> spawn fn -> raise "oops" end
#PID<0.61.0>
iex(4)>
01:09:44.474 [error] Process #PID<0.61.0> raised an exception
** (RuntimeError) oops
    :erlang.apply/2

nil
iex>
```

伝搬させるには spawn_link を使用する。

```ex
iex(6)> spawn_link fn -> raise "oops" end
** (EXIT from #PID<0.65.0>) an exception was raised:
    ** (RuntimeError) oops
        :erlang.apply/2

01:13:07.491 [error] Process #PID<0.72.0> raised an exception
** (RuntimeError) oops
    :erlang.apply/2

Interactive Elixir (1.2.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
```

- プロセスが死んでしまった際に検知し，新しいプロセスを動かすためにリンクを使用する。
   - デフォではプロセスとスーパーバイザー(監視役)間では何も共有しないため
- プロセスが独立しているからプロセス内での失敗が他のプロセスをクラッシュさせたり状態を壊したりすることはない

> 他の言語では例外は受けとったり操作することが求められますが，Elixirではスーパーバイザーがシステムを再起動してくれるであろうため，プロセスは失敗するがままでかまいません．"早く失敗する"はElixirでソフトウェアを書く際の一般的な指針です！

プロセスを殺してナンボの世界。

## 11.4 状態 - State

状態を持つときはプロセス上に持つ。
状態を持つプロセスを無限ループで立ち上げておく。

以下キーバリューストアの例

```ex:kv.exs
defmodule KV do
  # プロセス立ち上げ
  def start_link do
    {:ok, spawn_link(fn -> loop(%{}) end)}
  end

  # 最終的に loop/1 を呼び出してループしている
  defp loop(map) do
    receive do
      {:get, key, caller} ->
        send caller, Map.get(map, key)
        loop(map)
      {:put, key, value} ->
        loop(Map.put(map, key, value))
    end
  end
end
```

使ってみる

```ex:kv.exs
iex(6)> {:ok, pid} = KV.start_link
{:ok, #PID<0.69.0>}
iex(7)> send pid, {:get, :hello, self()}
{:get, :hello, #PID<0.61.0>}
iex(8)> flush
nil  # 存在していないので nil がかえる
:ok

iex(9)> send pid, {:put, :hello, :world}
{:put, :hello, :world}
iex(10)> send pid, {:get, :hello, self()}
{:get, :hello, #PID<0.61.0>}
```

PIDに名前つけてエイリアス的なこともできる

```ex
iex> Process.register(pid, :kv)
true
iex> send :kv, {:get, :hello, self()}
{:get, :hello, #PID<0.61.0>}
iex> flush
:world
```

## だがしかし

上でやってきたことはすでに Elixir が提供している。

```ex
iex> {:ok, pid} = Agent.start_link(fn -> %{} end)
{:ok, #PID<0.90.0>}
iex> Agent.update(pid, fn map -> Map.put(map, :hello, :world) end)
:ok
iex> Agent.get(pid, fn map -> Map.get(map, :hello) end)
:world

# 書き換える
iex> Agent.update(pid, fn map -> Map.put(map, :hello, :hogehoge) end)
:ok
iex> Agent.get(pid, fn map -> Map.get(map, :hello) end)
:hogehoge
```

## 参考

[11 プロセス - Processes](http://elixir-ja.sena-net.works/getting_started/11.html#11.3-リンク---links)