Erlang/OTPでは、gen_server(Generic Server Behaviour)のような便利なフレームワークが用意されています。これは、ビヘイビア(behaviour)という概念に基づいています。ビヘイビアを使ったプログラムは、
- ビヘイビア実装モジュール(ライブラリ側)
- コールバック・モジュール(ユーザー定義側)
の2つのモジュールから構成されます*1。
例えば、gen_serverのコールバック・モジュールでは、自分がgen_serverの規約に従うことを次の形で宣言します。
そして、必要なコールバック関数をエクスポートします。
-behaviour(gen_server).
-export([init/1, terminate/2, handle_call/3]).
-export([handle_cast/2, handle_info/2, code_change/3]).
さて、「ビヘイビアを自分で定義するにはどうしたらいいのだろうか?」と疑問だったんですが、gen_serverのソースを見たらすぐわかりました。例題として、状態マシンのフレームワークを作るとすれば、状態マシンのビヘイビア実装モジュールで次のように書きます。
-module(state_machine).-export([behaviour_info/1]).
behaviour_info(callbacks) ->
[
% is_symbol(term()) -> boolean()
{is_symbol, 1},
% is_acceptable_symbol(State, term()) -> boolean()
% State = term()
{is_acceptable_symbol, 2},% init() -> State
{init, 0},
% input(State, term()) -> NewState
{input, 2},
% is_final(State) -> boolean()
{is_final, 1}
];
behaviour_info(_Other) ->
undefined.
要するに、期待するコールバック関数達を {関数名, アリティ}
の形で並べるだけです。形式上は、behaviour_info/1という関数を定義しますが、意味的にはある種のインターフェース宣言です。この宣言に対応する関数定義がコンパイルされ、モジュールのBEAMファイルに入ります。
コールバック・モジュール側で -behaviour(state_machine).
と書くと、コンパイラはstate_machineモジュールのbehaviour_info/1を呼んで取得した情報をもとにチェックをします。ビヘイビアに宣言された関数がないと警告しますが、エラーにはなりません。
*1:2つ以上のモジュールで構成してもよいが、複雑化するから好ましくないだろう。