Elixir: GenServerという機能

# GenServerとは
プログラムを書いていると, 毎回とは言わないものの頻繁に出てくる設計のパターンに出くわすことが良くあります. 
いわゆるデザインパターンというやつですね.
Elixirでは, 様々パターンを実装する上で, 全部開発者が1から実装するのではなく,
そのパターンを半分だけ実装した状態のフレームワークを提供し, 開発者は対応するBehaviourを実装したモジュールを用意するだけで, パターンに基づいた実装を完成させることができる仕組みが用意されています.
(手前味噌ですが, Behaviourについての解説は[こちら](http://qiita.com/ak-ymst/items/51e4caf98ee4eef0d5b1)を御覧ください)

GenServerも, そういったElixirによって用意されたフレームワークの一つです.
では, どんな設計パターンを, フレームワーク化しているのでしょうか?
それは 
__クライアントプロセスから何らか形式のメッセージを受け取り, メッセージと状態を元に何らかの処理を行い, 結果を返す（もしくは返さない）__
という, 極めて単純で一般的な設計パターンです.

# GenSeverの使い方
[公式ドキュメント](http://elixir-lang.org/docs/stable/elixir/GenServer.html)のサンプルコードを使いながら GenServerの使い方を説明していきます.

```ex
defmodule Stack do
  use GenServer

  # Callbacks

  def handle_call(:pop, _from, [h|t]) do
    {:reply, h, t}
  end

  def handle_cast({:push, item}, state) do
    {:noreply, [item|state]}
  end
end

# Start the server
{:ok, pid} = GenServer.start_link(Stack, [:hello])

# This is the client
GenServer.call(pid, :pop)
#=> :hello

GenServer.cast(pid, {:push, :world})
#=> :ok

GenServer.call(pid, :pop)
#=> :world
```

用意すべきサーバプロセス用のモジュールは, まずGenServerモジュール(=Behaviour)をuseし

```ex
defmodule Stack do
  use GenServer

```

GenServerフレームワークから呼び出される(Callbackされる)関数を実装していきます.
GenServerモジュールで定義されているCallback関数は6つありますが, ここで実装されているのは
`handle_call`と`handle_cast`のみで, それ以外の関数はデフォルトの挙動のままにしています.

```ex
  def handle_call(:pop, _from, [h|t]) do
    {:reply, h, t}
  end

  def handle_cast({:push, item}, state) do
    {:noreply, [item|state]}
  end
end
```

`handle_call`は, クライアントからの送られてきたメッセージ, クライアントプロセスのpid, サーバプロセスの現在の状態を受け取り, `{:reply/返り値/新しい状態}`というタプルを返します.
返り値のタプルに新しい状態が含まれていることからわかるように, 次にこの関数が呼び出されるときは
引数の状態にはこの新しい状態が渡されます.

`handle_cast`は, クライアントからの送られてきたメッセージ, サーバプロセスの現在の状態を受け取り, `{:noreply/新しい状態}`というタプルを返します.

`handle_call`と`handle_cast`の違いは, クライアントに対して値を返すか否かであり, `handle_call`は値を返し, `handle_cast`は値を返しません.

このモジュールをサーバプロセスとして利用するやり方は, まずGenServerモジュールのstart関数を使ってプロセスを立ち上げ

```ex
# Start the server
{:ok, pid} = GenServer.start_link(Stack, [:hello])
```

あとは, 生成されたpidを使って, Stackの値が欲しい時は`GenServer.call`を, Stackに値を積みたいときは`GenServer.cast`を使ってメッセージを送っていきます.

```ex
# This is the client
GenServer.call(pid, :pop)
#=> :hello

GenServer.cast(pid, {:push, :world})
#=> :ok

GenServer.call(pid, :pop)
#=> :world
```

プロセス間で同期を取りながらメッセージをやり取りするには, まずサーバ側をrecieve状態にして
クライアントはメッセージをsendしたら, サーバからのメッセージを受け取るようにreceive状態にして...
と言った処理を書いてやる必要があるのですが, 見ての通りそんな記述は全くありません.
開発者は, ただ`GenServer.call`経由で呼び出される`handle_call`や, `GenServer.cast`経由で呼び出される`handle_cast`を実装するだけでいいのです.
(だからこそフレームワークなのですが)


# RemoteAgentをGenServerを使って実装し直す
[先日作成したRemoteAgent](http://qiita.com/ak-ymst/items/49bc44796281404024ba)は, クライアントからメッセージを受け取り, 状態を変えたり値を返したりするモジュールでした.
つまり, これはGenServerが提供しているフレームワークの処理の流れそのものということです. 
このモジュールをGenServerを使った形に書き換えていきます.

書き換え前のコードは以下のとおり.

```ex
defmodule RemoteAgent3 do

  @name :agent_register3

  def start(init \\ "") do
    pid = spawn(RemoteAgent3, :agent, [init])

    :global.register_name @name, pid

    :ok
  end

  def agent(state) do
    receive do
      {client, :get} ->
        send client, {:ok, state}
        agent(state)

      {client, :update, new_state} ->
        send client, {:ok, new_state}
        agent(new_state)

    end
  end

  def get() do
    send :global.whereis_name(@name), {self, :get}

    receive do
      {:ok, state} -> state
    end
  end

  def update(new_state) do
    send :global.whereis_name(@name), {self, :update, new_state}

    receive do
      {:ok, _} -> :ok
    end
  end

end
```

この中で, クライアントからのメッセージを処理している`agent`関数の中では...

```ex
  def agent(state) do
    receive do
      {client, :get} ->
        send client, {:ok, state}
        agent(state)

      {client, :update, new_state} ->
        send client, {:ok, new_state}
        agent(new_state)

    end
  end
```

`{client, :get}`というメッセージを受け取ったら現在の状態を返し, `{{client, :update, new_state}}`というメッセージを受け取ったら, 状態を更新しています.
これをそれぞれ`handle_call`と`handle_cast`で書き換えます.

```ex
  def handle_call(:get, _client, state) do
    {:reply, state, state}
  end

  def handle_cast({:update, new_state}, state) do
    {:noreply, new_state}
  end
```

試しにこの状態で動かしてみましょう.

```ex
iex(2)> {:ok, pid} = GenServer.start_link(RemoteAgent3, "init message")
{:ok, pid} = GenServer.start_link(RemoteAgent3, "init message")
{:ok, #PID<0.66.0>}
iex(3)> GenServer.call(pid, :get)
GenServer.call(pid, :get)
"init message"
iex(4)> GenServer.cast(pid, {:update, "updated message"})
GenServer.cast(pid, {:update, "updated message"})
:ok
iex(5)> GenServer.call(pid, :get)
GenServer.call(pid, :get)
"updated message"
```

この時点で機能としては使えていることがわかります.
ただ, 呼び出すたびに`GenServer.~~`と書かないといけないのも冗長なので, これらの記述をラップして, もう少し使いやすいインタフェイスを用意しましょう.

まずサーバプロセス開始の関数を用意します.

```ex
  def start(init \\ "") do
    GenServer.start_link(RemoteAgent3, init, name: @name)

    :ok
  end
```

次に, 値の取得/更新用にそれぞれ`get`関数/`update`関数を用意します.

```ex
  def get() do
    GenServer.call(@name, :get)
  end

  def update(new_state) do
    GenServer.cast(@name, {:update, new_state})
  end
```

説明が抜けていましたがGenServerの関数を使ってプロセスを立ち上げる際に, 引数として名前を渡してやることで
立ち上がったプロセスにその名前でアクセスすることができるようになります.
ここでは, その機能を使って, callやcastの呼び出しはpidではなくて名前を使ってプロセスを指定するようにしています.

モジュール全体はこんな感じです.

```ex
defmodule RemoteAgent3 do
  use GenServer

  @name :agent_register3

  def start(init \\ "") do
    GenServer.start_link(RemoteAgent3, init, name: @name)

    :ok
  end

  def get() do
    GenServer.call(@name, :get)
  end

  def update(new_state) do
    GenServer.cast(@name, {:update, new_state})
  end

  # GenServer Callback functions                                                                                                                                                                 
  def handle_call(:get, _client, state) do
    {:reply, state, state}
  end

  def handle_cast({:update, new_state}, state) do
    {:noreply, new_state}
  end

end
```
あとはこれらの関数を使っていくだけです.

```ex
iex(2)> RemoteAgent3.start("init message")
RemoteAgent3.start("init message")
:ok
iex(3)> RemoteAgent3.get
RemoteAgent3.get
"init message"
iex(4)> RemoteAgent3.update("updated message")
RemoteAgent3.update("updated message")
:ok
iex(5)> RemoteAgent3.get
RemoteAgent3.get
"updated message"
```

# まとめ
GenServerは便利
使えるときは積極的に使っていこう

# 参考
Elixir GenServer: http://elixir-lang.org/docs/stable/elixir/GenServer.html
Elixir の OTP (GenServer 編): http://qiita.com/naoya@github/items/ae17a8166e52fc463012