Erlangと副作用
関数型言語であるErlangは副作用を起こす操作は一般の文法上には存在していない(代入もcall/ccも無い)が、 それでもそのような結果を引き起こすことは可能だ。
Erlangで副作用を発生させる手段には2種類ある(他にもあったらやんわりと指摘頂きたい)。
- ドライバ
- gen_server
などのsupervisorによって管理されるworker群
ドライバは外部のオブジェクト(CやJavaやその他実行ファイル)とリストをやりとりするものだから、 当然副作用は起こりうる(というより、この場合は副作用を起こすことが目的である)ので、ここでは説明しない。
workerとして振る舞わせるには、例えば、
-behaviour(gen_server).
とプログラム中に書くことでgen_serverとして機能するようになる。
gen_serverとして書かれたコードは5つのコールバック関数を持ち、 supervisorから呼び出されるようになる。
Erlangでは、
Pid = spawn(?MODULE, foo, []), Pid ! Message, ...
のようなコードを用いて、サーバ側の状態の変更をさせるのが一般的なのだが、 gen_serverでは、そうする必要が無くなる(単にコールバックをトリガしてやるだけでよい)。 その代償として、 その「状態」を管理することができなくなる(状態の変更はgen_serverとsupervisorの間で行なわれる)。 これが副作用に見えるわけだ。
例を挙げる。
-module(inc).
-behaviour(gen_server).
-export([start_link/0]).
-export([inc/0]).
-export([init/1, handle_call/3]).
start_link() ->
gen_server:start_link({local, inc}, inc, [], []).
inc() ->
gen_server:call(?MODULE, inc).
init(_Args) ->
process_flag(trap_exit, true),
{ok, 0}.
handle_call(inc, _From, State) ->
Reply = State,
{reply, Reply, State + 1}.
先ほど5つのコールバック関数と書いたが、 ここではinit/1とhandle_call/3のみ使用した。
start_link/0とinc/0が利用者側のコードで、 他の関数は触ることができないようになっている。 状態の変更はhandle_call/3で行なっている。
実行結果は以下のようになる。
1> inc:start_link().
{ok,<x.x.x>}
2> inc:inc().
0
3> inc:inc().
1
4> inc:inc().
2
blog comments powered by Disqus