プログラミング言語 Erlang と その応用事例

48
プププププププププ Erlang プ ププププププ ププププ (HIYAMA Masayuki) 2008 プ 10 プ 23 プ ( プ )

description

プログラミング言語 Erlang と その応用事例. 檜山正幸 (HIYAMA Masayuki) 2008 年 10 月 23 日 ( 木 ). おおざっぱな予定. 前半: Erlang の紹介 18:30 - 前半の質疑応答 19:00 - 後半: Web Comunication Channels の紹介 19:15 - 後半の質疑応答 19:45 -. プログラミング言語 Erlang. Erlang は関数型言語 ? Erlang はプロセス指向言語 ! Erlang はメッセージ指向言語 ! Erlang は並列指向言語 ! - PowerPoint PPT Presentation

Transcript of プログラミング言語 Erlang と その応用事例

Page 1: プログラミング言語 Erlang と その応用事例

プログラミング言語Erlang とその応用事例

檜山正幸 (HIYAMA Masayuki)2008 年 10 月 23 日 ( 木 )

Page 2: プログラミング言語 Erlang と その応用事例

2

おおざっぱな予定

1. 前半: Erlang の紹介 18:30 - 2. 前半の質疑応答 19:00 -

3. 後半: Web Comunication Channels の紹介19:15 -

4. 後半の質疑応答 19:45 -

Page 3: プログラミング言語 Erlang と その応用事例

3

プログラミング言語 ErlangErlang は関数型言語? Erlang はプロセス指向言語! Erlang はメッセージ指向言語! Erlang は並列指向言語! Erlang は分散指向言語 Erlang のランタイムシステムはすごい!?

文末記号の意味:? 今日は強調しない。あまり触れない。 ! 強調したい。いろいろな言い方されるが根は一緒。 !? 確かにそうだが、よくわからない(からあまり触れない)。 無印 特に話題にしないが、推測できるでしょ(たぶん)。

Page 4: プログラミング言語 Erlang と その応用事例

(サンプルの部)

Page 5: プログラミング言語 Erlang と その応用事例

5

Erlang プログラムの編成:なんだかんだ

! モジュールは関数の集まり mod:func(arg1, arg2) モジュール名、ソースファイル名、実行可能( BEAM )ファイル名は同一

論理的な概念(モジュール)と物理的な概念(ファイル)を混同しても弊害は少ない

! ランタイムシステムには、モジュールが(原則)動的にロードされる

モジュールの集まりを”アプリケーション”と呼ぶ(要注意!)

Page 6: プログラミング言語 Erlang と その応用事例

6

Erlang プログラムの編成:なんだかんだ (2)

アプリケーションの物理的実体は、モジュールを格納しているディレクトリ

アプリケーションメタデータもあるが、小規模なアプリケーションではメタデータ不要

システムはアプリケーション(モジュール集合)と、どのアプリケーションにも属さない野良モジュールからなる。

パッケージ機構(モジュールに階層的名前空間を提供)もあるが、使っている例を見たことがない。

! 関数名は、同一モジュール内なら裸の名前、他のモジュールならコロンで修飾

import 機構があるが、「使わないほうがよい」とされている

Page 7: プログラミング言語 Erlang と その応用事例

7

Erlang プログラムの編成:だから結局

(図:ここでホワイトボードに図を描きます。)

Page 8: プログラミング言語 Erlang と その応用事例

8

Erlang のプロセス

どんな関数でも、とあるプロセスで実行される 換言すると、プロセスがないと関数が実行できない

一方、プロセスは 1 つの関数(メイン)を実行するためにある

もちろん、 1 つの関数から呼ばれた関数も同じプロセスで実行される

プロセスのメイン関数が終わればプロセスも終わる(消滅)Erlang では、関数だけでなく、プロセスが明白に意識され市民権を持ち、 いくらでも(誇張あり)プロセスを使える。

Page 9: プログラミング言語 Erlang と その応用事例

9

ここでなぜか HelloWorld%% d.erl -*- coding:utf-8 -*--module(hello).-compile(export_all).

do() -> spawn(fun()->io:format("Hello, world.~n") end).do(N) -> spawn(?MODULE, hello, [N]).

forever() -> spawn(fun helloever/0).

hello(0) -> ok;hello(N) when N > 0 -> io:format("Hello, world.~n"), timer:sleep(500), hello(N - 1).

helloever() -> io:format("Hello, world.~n"), timer:sleep(500), helloever().

Page 10: プログラミング言語 Erlang と その応用事例

ここでなぜか HelloWorld (2)ErlangShell (という名の対話的プロセス)から hello:do(). とすると: 1.Shell プロセスの管理下で hello:do() が実行される。

2.do() からプロセスが生成される。 3.生成されたプロセスにより、 1 回 Hello を表示する。 4.それだけでプロセスは正常終了。

hello:do(10). とすると: 1.基本的に同じことが起きるが、プロセスはやや長時間

の寿命を持つ。

10

Page 11: プログラミング言語 Erlang と その応用事例

ここでなぜか HelloWorld (3)再帰呼び出しに制限を設けず、長時間を無限に

伸ばすとリアクティブなプロセスになる。 寿命の観点から、プロセスを次の 2 種に分類す

るのが現実的: ある一定時間(予測はできなくても)後に仕事を終えるプロセス。

原則として無限に動き続け、なんかのはずみで終わるかもしれないプロセス。

11

Page 12: プログラミング言語 Erlang と その応用事例

Erlang のプロセスって何?

確かに OS プロセスの概念に近い「とっても小さなコンピュータ」という比喩が有効CPU (リダクション実行主体)とスタックとヒープ1個のプロセスは、 1個の CPU と 1 つのスタックで逐次的に動く

生まれて、動いて/働いて、消える数マイクロ秒で生成可能普通に数万から数十万/ランタイム参考: http://lab.klab.org/wiki/Erlang_Process 「 4000万個起動することが出来ました」

12

Page 13: プログラミング言語 Erlang と その応用事例

Erlang のプロセスって何? (2)実行コードは、関数/モジュールというまとまりに編成されている

スタック上で関数フレーム達が伸びたり縮んだりヒープにはデータの実体が確保されたり解放されたりこの小さなコンピュータ達はネットワーク環境にいる固有 ID (アドレス)を持っている名前(公開ホスト名)を持ってもいい/持たなくてもいい互いに直接的にいきなりパケット (?)通信できるパケットに例えたモノは Erlang ターム(任意)バッファリングする NIC(?) を持っている、メッセージキュー/メールボックス

13

Page 14: プログラミング言語 Erlang と その応用事例

Erlang のプロセスって何? (3)メモリ空間は互いに完全に分離されている原則的に(例外はあるが)共有メモリはない実行コード(関数)は共有、公共的で特に所有者プロセスはない

NIC に相当するメッセージキューへのポストは直列化される

メッセージ以外の相互作用はなく、プロセス達は独立に動作する

14

Page 15: プログラミング言語 Erlang と その応用事例

Erlang のプロセスって何? (4)各プロセス(小さなコンピュータ)は、ほぼ同一の性能

実際にはランタイム内で、できるだけ公平にスケジュールされる

リソース割り当て(空間の分配)とスケジューリング(時間の分配)の対象/単位が同じ

1個のプロセスを見てる限りは、並列性を意識する必要はない。

メッセージのデータは、異なるメモリ空間にコピーされると解釈される。実際はそうでもないけど(理由:イミュータブル)。

15

Page 16: プログラミング言語 Erlang と その応用事例

メッセージ・データの移動

図 - 移動(ホワイトボード)

16

Page 17: プログラミング言語 Erlang と その応用事例

メッセージング・グラフ

理論的には完全有効グラフだが、相手を知ってないとメッセージを送れないので、 「知っている」関係で通信の経路は制限される。

メッセージデータは、特定時点のメッセージンググラフの辺に沿って伝送される。 全プロセスをノードするメッセージング・グラフは時々刻々と変化する。

図 -メッセージング (ホワイトボード)

17

Page 18: プログラミング言語 Erlang と その応用事例

リンクとシグナルのグラフ

リンクされたプロセスは、片方が死ぬともう一方も死ぬ

exit シグナル、 error シグナルがリンクに沿って伝搬

シグナルはメッセージと別物だが、メッセージとして捕捉可能

18

リンクの設定は、「 link するほう→されるほう」で方向を持つ。が、シグナルはその方向に無関係。

Page 19: プログラミング言語 Erlang と その応用事例

リンクとシグナルのサンプル

19

%% d06.erl -*- coding:utf-8 -*--module(d06).-compile(export_all).

start() -> spawn(fun parent_main/0).

parent_main() -> process_flag(trap_exit, true), Pid = spawn_link(fun child_loop/0), parent_loop(Pid).

Page 20: プログラミング言語 Erlang と その応用事例

リンクとシグナルのサンプル (2)

20

parent_loop(Pid) -> receive s -> % stop exit(Pid, kill), % 通常、 killは乱用してはいけない io:fwrite("Parent: byebye.~n"), exit(normal); % 明示的に exitを呼んでもよい

{'EXIT', Pid, Why} -> % シグナルから変換されたメッセージ io:fwrite("Parent: Child ~p exited. :~p~n", [Pid, Why]), NewPid = spawn_link(fun child_loop/0), parent_loop(NewPid);

Message -> Pid ! Message, % そのまま子供に転送 parent_loop(Pid) end.

Page 21: プログラミング言語 Erlang と その応用事例

リンクとシグナルのサンプル (3)

21

child_loop() -> receive {error, Term} -> io:fwrite("Child ~p: 'error' received.~n", [self()]), erlang:error(Term), child_loop(); {exit, Term} -> io:fwrite("Child ~p: 'exit' received.~n", [self()]), exit(Term), child_loop(); {throw, Term} -> io:fwrite("Child ~p: 'throw' received.~n", [self()]), throw(Term), child_loop(); Other -> io:fwrite("Child ~p: Unknown message:~p~n", [self(), Other]), child_loop() end.

Page 22: プログラミング言語 Erlang と その応用事例

よく出来た資料があるので拝借

特に、アニメートするプロセスの図がわかりやすい。 Erlang.ppt

22

Page 23: プログラミング言語 Erlang と その応用事例

Erlang でアプリケーションを作るには

関数達の静的な構造はモジュール(+アプリケーション)として編成

実行時のプロセス達の生成消滅のシナリオを考える

メッセージグラフ、リンクグラフとしてプロセス達の相互関係を考える

メッセージのプロトコル、シグナルのプロトコルを考える

23

Page 24: プログラミング言語 Erlang と その応用事例

Erlang でアプリケーションを作るには (2)

基本的なフレームワークは OTP ライブラリが準備している。

メッセージプロトコルは、 RPC の API やイベント配信として考えるとよい

高水準の API を(例えば IDL で)決めれば、あとはOTP が面倒みてくれる

参照: http://d.hatena.ne.jp/m-hiyama/20070712/1184213007

リンクグラフの構成とシグナルのプロトコルも OTPが準備している(ワーカー/スーパーバイザ・モデル)

24

Page 25: プログラミング言語 Erlang と その応用事例

Erlang でアプリケーションを作るには (3)

図(ホワイトボード) 基本素材:関数、プロセス、メッセージ 高級でマクロな素材: RPC 、イベント、クラアント/サーバ、スーパーバイザ・ツリー

ユーザーレベルでの API 、プロトコル アプリケーション

25

Page 26: プログラミング言語 Erlang と その応用事例

Web Communication Channels

の紹介

http://www.microapplications.net26

Page 27: プログラミング言語 Erlang と その応用事例

ことの発端

Web アプリケーションとはいうが作る(作らせる)のは Web サイト所有者ブラウザ利用者はサイトと独立にアプリケーションを作れないの?

27

Page 28: プログラミング言語 Erlang と その応用事例

ブラウザ上のアプリケーション

GUI は HTML レンダリングエンジン実装言語は JavaScriptストレージがない実は自発的に通信もできないAjax は?サイトに依存し縛られる

28

Page 29: プログラミング言語 Erlang と その応用事例

通信機能とストレージ

中立な中継サイトを設けて、できるだけ透過的にブラウザ -ブラウザ通信をサポートする。

永続的なストレージも提供する。クロスドメイン HTTP通信が必須現状、変な方法しかない

悲しいがしょうがない逆方向(サーバー ⇒ ブラウザ)通信も必要

COMET参考: http://d.hatena.ne.jp/m-hiyama/20080528/1211950144

29

Page 30: プログラミング言語 Erlang と その応用事例

別な動機

サーバー側も JavaScript で書けたらいいじゃない? 既にあるけど流行ってない

JavaScript プログラムがローミングしたら面白いのでは?

そりゃ無理でしょJavaScript の小さな小さなサブセットくらいなら、、、、

この未練は今でも尾を引いている

30

Page 31: プログラミング言語 Erlang と その応用事例

Erlang がお手本

単に実装言語としてだけでなく、分散モデルも借用。

31

Erlang WCCノード ブラウザもノードプロセス エージェント(動作

主体)PID エージェント

ID ( AID )メッセージ マイクロ・メッセー

ジターム JSON データgen_server Call RGenic Callgen_server Cast RGenic Cast

Page 32: プログラミング言語 Erlang と その応用事例

アリモノをツギハギ

JavaScript のイベントモデルは W3C DOM3 から

外部からのコールバックは ActionScript のExternalInterface

データ形式は徹底的に JSON 、ただし抽象的データ形式として

OMG IDL のサブセットで仕様記述の予定(全然できてない)

32

100% コンフォーマントはめざしてないが、それでも標準と折り合いを付けるのは大変。用語の混乱やネーミングの理不尽さに泣く。

Page 33: プログラミング言語 Erlang と その応用事例

COMET原理は簡単やってみると、ブラウザごとの挙動の違いに泣くJavaScript のシングルスレッドにも泣く -- IO ブロックとか sleep してポールとかができない

On Demand JavaScript 方式( a.k.a JSONP )と COMET を組み合わせて、なんとか双方向通信

33

Page 34: プログラミング言語 Erlang と その応用事例

COMET は資源を消費する

が、 Erlang なら、、、 http://www.sics.se/~joe/apachevsyaws.html

Apache vs. YawsApache が同時接続数約4千で応答なし、 Yaws 同時接続数 8 万以上まで応答。

http://groups.google.com/group/comp.lang.functional/msg/33b7a62afb727a4f?dmode=source2000万個のプロセス (64-bit erlang on a 1.5 GHz SPARC with 16 GB RAM)

34

Page 35: プログラミング言語 Erlang と その応用事例

Erlang で作ったことは

たしかに考えやすい、作りやすい他の言語とものすごく変わるわけではないスレッドのような難しさはない静的な型概念、型チェックがないのは痛いときもある

YAWS というプラットフォームもあることだし、 Web アプリケーションには向いているのではないか

逆に、 Web 的手法が Erlang プログラミングに生かせることもある

35

Page 36: プログラミング言語 Erlang と その応用事例

Erlang で作ったことは (2)500 メッセージ/秒くらいはさばけそうフォールトトレランスや実行時コード置き換えは今後

最近やっと「不要なことは書かない」「いさぎよく死ぬ」「一人で死ぬ」が分かってきた

これは、 Erlang のポリシーで納得が難しい部分

36

Page 37: プログラミング言語 Erlang と その応用事例

アプリケーション

ジャンケン大会が最初設定した目標だった多人数でやるクイズやゲーム同時アンケート/投票ブラウザ・サーバー(今日、「神社を作れ」と)ブラウザ不在時はスクリプトで動く

37

Page 38: プログラミング言語 Erlang と その応用事例

問題

クロスドメイン通信のような基盤があやしいセキュリティ :よくわかんないモラル : これも問題になるかもしれないクラスター構成 : まだやってないので未知監視 : ライブラリがあれども事例がないなにがうれしい : さあ?

← 大問題では

38

Page 39: プログラミング言語 Erlang と その応用事例

(おわり)

Page 40: プログラミング言語 Erlang と その応用事例

サンプルの部

Page 41: プログラミング言語 Erlang と その応用事例

41

まずはサンプルとデモ

• 細かいことは気にしない。 • なんとなく雰囲気がわかればよい。 • 後でまた説明します。

Page 42: プログラミング言語 Erlang と その応用事例

42

関数を定義してみる

%% d01.erl -*- coding:utf-8 -*-%% 再帰関数

-module(d01). % モジュールの宣言-export([fact/1, append/2]). % エクスポートする関数の宣言

%% 階乗fact(0) -> 1; % Prologerはセミコロンである点に注意fact(N) when N > 0 -> % when ... はガード N * fact(N -1).

%% リストの連接append([], List) -> List;append([First|Rest], List) -> [First|append(Rest, List)].

Page 43: プログラミング言語 Erlang と その応用事例

43

データとしての関数( fun )

%% d02.erl -*- coding:utf-8 -*-%% 高階関数、ラムダ式

-module(d02).-compile(export_all). % コンパイラに全部エクスポートするように指令

sumup(From, To, Fun) -> % 第 3 引数に fun lists:sum( lists:map(Fun, lists:seq(From, To))).

make_inc(N) -> % 戻り値が fun fun (X) -> X + N end. % '->' を忘れるのだ(檜山だけ?)

sq(N) -> % テスト用、平方する関数 N * N.

Page 44: プログラミング言語 Erlang と その応用事例

44

いたるところにパターンマッチ%% d03.erl -*- coding:utf-8 -*-%% パターンマッチ-module(d03).-compile(export_all).

p1({Name, Age}) -> % 既にお馴染み、引数にパターン io:format("Your name: ~s~n", [Name]), % カンマは順次実行 io:format("Your age: ~p~n", [Age]).

p2({Name, Age}) when is_list(Name), is_number(Age) -> % ガードで制約をきつく io:format("Your name: ~s~n", [Name]), io:format("Your age: ~p~n", [Age]);p2(Name) when is_list(Name) -> % 別なパターン&ガード io:format("Your name: ~p~n", [Name]).

p3(X) -> % p3 と同じ、 case式はもっともよく使われる制御構造 case X of Name when is_list(Name) -> io:format("Your name: ~s~n", [Name]); {Name, Age} when is_list(Name), is_number(Age) -> io:format("Your name: ~s~n", [Name]), io:format("Your age: ~p~n", [Age]) end.

Page 45: プログラミング言語 Erlang と その応用事例

45

プロセス作ってメッセージ送ってみる

%% d05.erl -*- coding:utf-8 -*-%% 自発的に動き続けるプロセス-module(d05).-compile(export_all).

start() -> spawn(fun main/0).

main() -> receive b -> % break break(); % 再帰じゃないけど末尾呼び出し (last call) s -> % stop io:format("stop.~n"); % プロセスも自然終了 _Other -> main() % キューのフラッシュ after 500 -> % 0.5秒ごとに io:format("Hello.~n"), main() % 末尾再帰 end.

break() -> receive c -> % continue main(); % これで main に戻る _Other -> io:format("break and stop.~n") % 終わり end.

Page 46: プログラミング言語 Erlang と その応用事例

46

リアクティブなプロセス

%% d05.erl -*- coding:utf-8 -*-%% 自発的に動き続けるプロセス-module(d05).-compile(export_all).

start() -> spawn(fun main/0).

main() -> receive b -> % break break(); % 再帰じゃないけど末尾呼び出し (last call) s -> % stop io:format("stop.~n"); % プロセスも自然終了 _Other -> main() % キューのフラッシュ after 500 -> % 0.5秒ごとに io:format("Hello.~n"), main() % 末尾再帰 end.

break() -> receive c -> % continue main(); % これで main に戻る _Other -> io:format("break and stop.~n") % 終わり end.

Page 47: プログラミング言語 Erlang と その応用事例

47

なぜか Java クラスを出してみる

/* Counter.java */public class Counter { private int count; // 内部状態

public Counter(int init) { count = init; } public void up() { count++; // 状態の変更 } public void down() { count--; // 状態の変更 } public int value() { return count; // 問い合わせに応える }}

Page 48: プログラミング言語 Erlang と その応用事例

48

Erlang ならこうなる%% counter.erl -*- coding:utf-8 -*-%% カウンタ-module(counter).-compile(export_all). % お行儀悪い

%% new Constructor(init) 相当start(Init) -> spawn(?MODULE, main, [Init]).

start(Name, Init) -> register(Name, spawn(?MODULE, main, [Init])).

main(Count) -> receive up -> % up() 相当 main(Count + 1); down -> % down() 相当 main(Count - 1); {value, Pid} -> % value() 相当 Pid ! {count, Count}, % 値をメッセージで返す main(Count); s -> % 終了 ok; _Other -> % 無視 main(Count) end.