※ 見出しの「一月」はヒトツキ、イチガツじゃないよ。
一ヶ月ほど前、Programming Tools and Techniques の会(2008年10月23日(木)18時30分 早稲田大学情報理工学科)で、Erlangと応用事例の話をさせていただきました。そのときの資料を以下のURLに置いておきます。
- http://www.chimaira.org/archive/PTT348_Erlang.ppt スライド
- http://www.chimaira.org/archive/PTT348_Sources.pdf 参考ソース
実は、ちょっと手直して、とか思っていたのですが、いつまでたってもやりそうにないので、腐ってしまう前にそのまま公開します。スライド内で引用されているErlang.pptは、http://www.erlang.se/publications/Erlang.ppt です。
全体で90分、質疑応答でだいぶ時間を使ったので、スライドにあることを全部しゃべったわけではありません。特に、後半の Web Communication Channels は簡単なデモをしただけでした。機会があれば、もう少し説明したいと思っています。
質疑応答
http://www.ci.i.u-tokyo.ac.jp/~sasada/ptt/arc/348/index.html にあるものと同じです。ごくわずかな字句修正をしています。
Q. リンクする,されるというのはどうするのか?
プロセスのリンクですね。2つの方法があります。
まず、すでに存在するプロセスにリンクしたいときは、リンクしたい相手のPID(プロセス識別子)を指定して、link/1 という組み込み関数を呼びます。リンクを解除したいときは unlink/1 です。
2つ目は、新しくプロセスを生成すると同時に親プロセスとのリンクも作る方法です。spawn_link(アリティは1,2,3,4がある)を使います。
Q. プロセスが死んだらデバッグはできるのか?
ランタイムエラーではスタックトレースが生成されるので、これを目安に検死はできます。が、注意すべき点があります。
1つは、せっかく生成されたスタックトレースを(例外の中継のときなど)プログラマが捨ててしまうことです。例外やシグナルの処理のとき、スタックトレースを保存するようにプログラミングしないといけません。
2つめの注意は、末尾呼び出し最適化が働くと、呼び出し履歴がスタックに残らないことです。スタックトレースをみても、クラッシュの直前の状況が不明なときがあります。
Q. プログラムで明示的に処理しないタームを使うことがあるのか
タームとはメッセージデータの意味ですよね。
プロセスがメッセージキューからメッセージを取り出さない限り、メッセージは単に貯まるだけです。ランタイムシステムがプロセスに代わってメッセージ処理をすることはありません。
ライブラリ/フレームワークが、一部のメッセージを処理してくれることはあります。そのようなライブラリ/フレームワークを利用しているときは、アプリケーションレベルのプログラムで明示的に書かなくてもメッセージ処理がされることになります。
Q. メッセージがたまった状態で main の処理を変えることはできるのか
できます。ただし、できるようにプログラミングしておかないと無理です。
自前でそのように書くこともできますが、Erlang配布に付属しているOTPライブラリを使うほうがいいと思います。OTPの規約に従うだけで、コードチェンジをサポートできます。
Q. 実行中にプログラムを変更することができるのか
できます。それができることは、Erlangの大きな設計目標ですから。エリクソンの交換機は、バグフィックスなどを行いながら、5年、10年単位で止まらずに動いているとのことです。
ここでのプログラム変更は、バグフィックスやリファクタリングであり、当然ながら、他のプログラムに影響するような仕様変更は困難です。メッセージにアプリケーションやプロトコルのバージョンを埋め込み、新旧のバージョンを同時に動かしながら徐々に移行するような手順を実行できるかも知れませんが、なかなかに大変だろうと予想します。
Q. 関数をメッセージとして送ることは可能か
可能です。ラムダ式が送られると考えればいいでしょう。ただし、途中まで評価された状態(継続のようなもの)を送るわけではなくて、関数定義を字面で送ると考えてください。
ラムダ式(Erlang用語ではfun)が生成されたときの変数環境(束縛の状態)も一緒に詰め込まれるとも言えますが、Erlangの変数は事実上定数なので、最初から定数で書いたラムダ式が送られるのと同じことです。
%% qa.erl-module(qa).
-compile(export_all).start() ->
spawn(fun main/0).main() ->
receive
Fun when is_function(Fun) ->
% Funは整数1引数だと仮定する
io:format("Fun(1) = ~p~n", [Fun(1)]),
main();
_Other ->
% メッセージを捨てる
main()
end.send(Pid, Y) ->
Pid ! (fun(X)-> X + Y end).%% 実行例
%%
%% > c(qa).
%% > f(). % 念のため
%% > P = qa:start().
%% > P! fun(X)->X+10 end.
%% > qa:send(P, 3).
Q. IOみたいなものをメッセージで送ると変なことは起こらないか
IOとはいっても、Erlangの場合はプロセスでラップしてます。例えば、Erlangからみたファイルはファイルとして振る舞うプロセスで、ストリームもメッセージで模倣されます。
「プロセスとしてのIO」なら送っても問題ないと思います。しかし、より低水準のポート(ポートの先はOSプロセスなど)を他のマシンに送って操作すると、なにか具合が悪いことが起こるかも知れません。使ったことも試したこともありませんが、やらないほうがいいでしょう(プロセスでラップすべき)。
Q. supervisor がバグってたらどうなるのか
すべてが台無しです。OTP付属のスーパーバイザーはバグってないと信じてますし、信じるしかないでしょう。自分でスーパーバイザーを書くのはお勧めできません。
Q. データベース用のライブラリはあるのか
エムネジア(MNESIA)と呼ばれる分散データベースが付属してます。
これは、通常のRDBやらSQLと比較すべきものではありません。機能的には、任意のタームをキーに使えるハッシュマップといったところです。あまり複雑なことはできません。
しかし、分散環境(クラスター構成)では、自動的にレプリケーションが行われるので、特定目的(そもそもが交換機用)では安全で信頼できるストレージとして便利だと思います。
MySQLなどの既存RDBの利用法は、他のプログラミング言語と大差ありません。
Q. プロセスのローミングはできるのか
実行中のプロセスを他のノードへとマイグレーションさせるという意味のローミングはできません。
あるプロセスが死んだとき、同じ役割りのプロセスを他のノードや他のマシンで生成して「仕事」を引き継ぐことはできます。しかしこれは、仕事が引き継がれるのであって、プロセスが移動したことにはなりません。
Q. プロセスのGCはあるのか
誰からも相手にされないでくすぶっているプロセスを殺すようなことですね。あると便利だと思うし、現実に欲しいのですが、ありません。
現状では、自前でPIDをテーブルに登録して管理するような方法しかありません。そもそも、どのプロセスが不要か/無意味かの判断が難しいと思います。アプリケーションロジックの観点からでないと、殺してもいいプロセスの判断ができない気もします。
Q. プロセスにマークすれば、GCもなんとかなったりしないか
データのポインタにあたるものが実体としてありません。メッセージング・グラフは概念的なもので、実行時に取れる情報からメッセージング・グラフを描く手段はありません(少なくとも私は知りません)。したがって、メッセージング・グラフの孤立ノードや離れ小島を検出できません。
ただし、上でも言ったように、アプリケーションレベルでグラフの辺に相当する実体を準備すればハナシは別ですが。
Q. 仕事で使っているのか.使っているならどこで使われているのか
私は趣味(?)で使ってます。いまだ仕事とはいえません。
本家エリクソンの交換機以外の利用はあまり知りません。Webアプリケーションのバックエンドには向いていると思います。
すごく著名なものを挙げておきます。
- http://wings.sourceforge.net/ 3Dモデラー
- http://www.ejabberd.im/ インスタントメッセージング・サーバー
- http://www.vendetta-online.com/ マルチプレイヤー・オンラインゲーム
Q. メッセージがたまったらどうなるか
どんどんたまるだけです。いずれは悪いことが起きるでしょう。
Q. ハードウェアが落ちたらどうなるか
もうどうにもなりません。
ノード(1つのランタイムシステム)が生きていることはハートビートで監視できます。ノードが死んだとは判断できますが、それがErlangプログラムのせいか、OSが落ちたのか、ハードウェアが壊れたのかは分かりません。
前もって、ノードが落ちたらどうすべきかの対策がなされていれば、1つのハードウェアが壊れても、クラスター全体としての機能を維持することはできるでしょう。