Elixir Processes

# Elixir Processes

## 概要
Elixir の Processes について。

Exlixir のコードは Process の中で動いている。
Process はそれぞれ独立している。

## Spawn
Process の生成の基本は、自動で import される spawn/1 関数を利用する方法です。

~~~
iex> pid = spawn fn -> IO.puts 1 * 2 end
2
#PID<0.76.0>
iex> self
#PID<0.54.0>
iex> Process.alive? pid
false
iex> Process.alive? self
true
iex> Process.info self
[current_function: {Process, :info, 1}, initial_call: {:erlang, :apply, 2},
 status: :running, message_queue_len: 0, messages: [], links: [],
# 略
  fullsweep_after: 65535, minor_gcs: 10], suspending: []]
~~~

## send and receive
send/2 関数でメッセージ送信
receive/1 関数でメッセージ受信

~~~
defmodule ProcessTest do
  def do_receive do
    receive do
      {:key, msg} -> "match " <> msg
      {:other, msg} -> "not match" <> msg
    after
      # wait 1 sec
      1_000 -> "after 1sec"
    end
  end
end

send self, {:key, "value"}
send self, {:other, "other"}
IO.puts ProcessTest.do_receive
IO.puts ProcessTest.do_receive
IO.puts ProcessTest.do_receive
~~~

* 出力

~~~
$ elixir process1.exs
match value
not matchother
after 1sec
~~~

* flush

~~~
iex  > send self, {:key, "value"}
{:key, "value"}
iex  > send self, {:other, "value"}
{:other, "value"}
iex  > flush
{:key, "value"}
{:other, "value"}
:ok
~~~

## Links
Process のリンクには spawn_link/1 を利用する。

### spawn で例外が発生した場合
例外が伝搬されない

~~~
iex  >  spawn fn -> raise "oops" end
#PID<0.70.0>

04:08:03.298 [error] Error in process <0.70.0> with exit value: {#{'__exception__'=>true,'__struct__'=>'Elixir.RuntimeError',message=><<4 bytes>>},[{erlang,apply,2,[]}]}
~~~

### spawn_link で例外が発生した場合
例外が伝搬される

~~~
iex  >  spawn_link fn -> raise "hoge raise" end

04:07:47.831 [error] Error in process <0.67.0> with exit value: {#{'__exception__'=>true,'__struct__'=>'Elixir.RuntimeError',message=><<10 bytes>>},[{erlang,apply,2,[]}]}


** (EXIT from #PID<0.54.0>) an exception was raised:
    ** (RuntimeError) hoge raise
        :erlang.apply/2

Interactive Elixir (1.0.0) - press Ctrl+C to exit (type h() ENTER for help)
~~~

## States

~~~
iex  > defmodule KV do
...  >   def start do
...  >     {:ok, spawn_link(fn -> loop(%{}) end)}
...  >   end
...  >
...  >   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
{:module, KV,
 <<70, 79, 82, 49, 0, 0, 6, 144, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 94, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115, 95, 118, 49, 108, 0, 0, 0, 2, 104, 2, ...>>,
 {:loop, 1}}
iex  >
nil
iex  > {:ok, pid} = KV.start
{:ok, #PID<0.76.0>}
iex  > send pid, {:get, :hoge, self()}
{:get, :hoge, #PID<0.54.0>}
iex  > flush
nil
:ok
iex  > send pid, {:put, :hoge, :world}
{:put, :hoge, :world}
iex  > send pid, {:get, :hoge, self()}
{:get, :hoge, #PID<0.54.0>}
iex  > flush
:world
:ok
iex  > send pid, {:put, :hoge, :hoge_world2}
{:put, :hoge, :hoge_world2}
iex  > send pid, {:put, :hige, :higeworld2}
{:put, :hige, :higeworld2}
iex  > send pid, {:get, :hoge, self()}
{:get, :hoge, #PID<0.54.0>}
iex  > send pid, {:get, :hige, self()}
{:get, :hige, #PID<0.54.0>}
iex  > flush
:hoge_world2
:higeworld2
:ok
~~~

* プロセスを登録する方法

~~~
{:ok, pid} = KV.start
Process.register(pid, :kv)
send :kv, {:get, :hello, self()}
~~~

## 参照
http://elixir-lang.org/docs/stable/elixir/Process.html
http://elixir-lang.org/getting_started/11.html