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(). 2blog comments powered by Disqus