1 / 48

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

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

loren
Télécharger la présentation

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

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. プログラミング言語Erlangとその応用事例 檜山正幸 (HIYAMA Masayuki) 2008年10月23日(木)

  2. おおざっぱな予定 • 前半:Erlangの紹介 18:30- • 前半の質疑応答 19:00- • 後半:Web Comunication Channelsの紹介19:15- • 後半の質疑応答 19:45-

  3. プログラミング言語Erlang • Erlangは関数型言語? • Erlangはプロセス指向言語! • Erlangはメッセージ指向言語! • Erlangは並列指向言語! • Erlangは分散指向言語 • Erlangのランタイムシステムはすごい!? 文末記号の意味: ? 今日は強調しない。あまり触れない。 ! 強調したい。いろいろな言い方されるが根は一緒。 !? 確かにそうだが、よくわからない(からあまり触れない)。 無印 特に話題にしないが、推測できるでしょ(たぶん)。

  4. (サンプルの部)

  5. Erlangプログラムの編成:なんだかんだ • ! モジュールは関数の集まり mod:func(arg1, arg2) • モジュール名、ソースファイル名、実行可能(BEAM)ファイル名は同一 • 論理的な概念(モジュール)と物理的な概念(ファイル)を混同しても弊害は少ない • ! ランタイムシステムには、モジュールが(原則)動的にロードされる • モジュールの集まりを”アプリケーション”と呼ぶ(要注意!)

  6. Erlangプログラムの編成:なんだかんだ (2) • アプリケーションの物理的実体は、モジュールを格納しているディレクトリ • アプリケーションメタデータもあるが、小規模なアプリケーションではメタデータ不要 • システムはアプリケーション(モジュール集合)と、どのアプリケーションにも属さない野良モジュールからなる。 • パッケージ機構(モジュールに階層的名前空間を提供)もあるが、使っている例を見たことがない。 • ! 関数名は、同一モジュール内なら裸の名前、他のモジュールならコロンで修飾 • import機構があるが、「使わないほうがよい」とされている

  7. Erlangプログラムの編成:だから結局 (図:ここでホワイトボードに図を描きます。)

  8. Erlangのプロセス • どんな関数でも、とあるプロセスで実行される • 換言すると、プロセスがないと関数が実行できない • 一方、プロセスは1つの関数(メイン)を実行するためにある • もちろん、1つの関数から呼ばれた関数も同じプロセスで実行される • プロセスのメイン関数が終わればプロセスも終わる(消滅) 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().

  10. ここでなぜかHelloWorld (2) • ErlangShell(という名の対話的プロセス)から hello:do(). とすると: • Shellプロセスの管理下で hello:do() が実行される。 • do()からプロセスが生成される。 • 生成されたプロセスにより、1回Helloを表示する。 • それだけでプロセスは正常終了。 • hello:do(10). とすると: • 基本的に同じことが起きるが、プロセスはやや長時間の寿命を持つ。

  11. ここでなぜかHelloWorld (3) • 再帰呼び出しに制限を設けず、長時間を無限に伸ばすとリアクティブなプロセスになる。 • 寿命の観点から、プロセスを次の2種に分類するのが現実的: • ある一定時間(予測はできなくても)後に仕事を終えるプロセス。 • 原則として無限に動き続け、なんかのはずみで終わるかもしれないプロセス。

  12. Erlangのプロセスって何? • 確かにOSプロセスの概念に近い • 「とっても小さなコンピュータ」という比喩が有効 • CPU(リダクション実行主体)とスタックとヒープ • 1個のプロセスは、1個のCPUと1つのスタックで逐次的に動く • 生まれて、動いて/働いて、消える • 数マイクロ秒で生成可能 • 普通に数万から数十万/ランタイム • 参考: http://lab.klab.org/wiki/Erlang_Process 「4000万個起動することが出来ました」

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

  14. Erlangのプロセスって何? (3) • メモリ空間は互いに完全に分離されている • 原則的に(例外はあるが)共有メモリはない • 実行コード(関数)は共有、公共的で特に所有者プロセスはない • NICに相当するメッセージキューへのポストは直列化される • メッセージ以外の相互作用はなく、プロセス達は独立に動作する

  15. Erlangのプロセスって何? (4) • 各プロセス(小さなコンピュータ)は、ほぼ同一の性能 • 実際にはランタイム内で、できるだけ公平にスケジュールされる • リソース割り当て(空間の分配)とスケジューリング(時間の分配)の対象/単位が同じ • 1個のプロセスを見てる限りは、並列性を意識する必要はない。 • メッセージのデータは、異なるメモリ空間にコピーされると解釈される。実際はそうでもないけど(理由:イミュータブル)。

  16. メッセージ・データの移動 図-移動(ホワイトボード)

  17. メッセージング・グラフ • 理論的には完全有効グラフだが、相手を知ってないとメッセージを送れないので、 「知っている」関係で通信の経路は制限される。 • メッセージデータは、特定時点のメッセージンググラフの辺に沿って伝送される。 全プロセスをノードするメッセージング・グラフは時々刻々と変化する。 • 図-メッセージング (ホワイトボード)

  18. リンクとシグナルのグラフ • リンクされたプロセスは、片方が死ぬともう一方も死ぬ • exitシグナル、errorシグナルがリンクに沿って伝搬 • シグナルはメッセージと別物 • だが、メッセージとして捕捉可能 リンクの設定は、「linkするほう→されるほう」で方向を持つ。が、シグナルはその方向に無関係。

  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).

  20. リンクとシグナルのサンプル (2) 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.

  21. リンクとシグナルのサンプル (3) 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.

  22. よく出来た資料があるので拝借 • 特に、アニメートするプロセスの図がわかりやすい。 • Erlang.ppt

  23. Erlangでアプリケーションを作るには • 関数達の静的な構造はモジュール(+アプリケーション)として編成 • 実行時のプロセス達の生成消滅のシナリオを考える • メッセージグラフ、リンクグラフとしてプロセス達の相互関係を考える • メッセージのプロトコル、シグナルのプロトコルを考える

  24. Erlangでアプリケーションを作るには (2) • 基本的なフレームワークはOTPライブラリが準備している。 • メッセージプロトコルは、RPCのAPIやイベント配信として考えるとよい • 高水準のAPIを(例えばIDLで)決めれば、あとはOTPが面倒みてくれる • 参照: http://d.hatena.ne.jp/m-hiyama/20070712/1184213007 • リンクグラフの構成とシグナルのプロトコルもOTPが準備している(ワーカー/スーパーバイザ・モデル)

  25. Erlangでアプリケーションを作るには(3) • 図(ホワイトボード) • 基本素材:関数、プロセス、メッセージ • 高級でマクロな素材:RPC、イベント、クラアント/サーバ、スーパーバイザ・ツリー • ユーザーレベルでのAPI、プロトコル • アプリケーション

  26. Web Communication Channelsの紹介 http://www.microapplications.net

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

  28. ブラウザ上のアプリケーション • GUIはHTMLレンダリングエンジン • 実装言語はJavaScript • ストレージがない • 実は自発的に通信もできない • Ajaxは? • サイトに依存し縛られる

  29. 通信機能とストレージ • 中立な中継サイトを設けて、できるだけ透過的にブラウザ-ブラウザ通信をサポートする。 • 永続的なストレージも提供する。 • クロスドメインHTTP通信が必須 • 現状、変な方法しかない • 悲しいがしょうがない • 逆方向(サーバー ⇒ブラウザ)通信も必要 • COMET • 参考: http://d.hatena.ne.jp/m-hiyama/20080528/1211950144

  30. 別な動機 • サーバー側もJavaScriptで書けたらいいじゃない? • 既にあるけど流行ってない • JavaScriptプログラムがローミングしたら面白いのでは? • そりゃ無理でしょ • JavaScriptの小さな小さなサブセットくらいなら、、、、 • この未練は今でも尾を引いている

  31. Erlangがお手本 • 単に実装言語としてだけでなく、分散モデルも借用。

  32. アリモノをツギハギ • JavaScriptのイベントモデルはW3C DOM3から • 外部からのコールバックはActionScriptのExternalInterface • データ形式は徹底的にJSON、ただし抽象的データ形式として • OMG IDLのサブセットで仕様記述の予定(全然できてない) 100%コンフォーマントはめざしてないが、それでも標準と折り合いを付けるのは大変。 用語の混乱やネーミングの理不尽さに泣く。

  33. COMET • 原理は簡単 • やってみると、ブラウザごとの挙動の違いに泣く • JavaScriptのシングルスレッドにも泣く -- IOブロックとかsleepしてポールとかができない • On Demand JavaScript方式(a.k.a JSONP)とCOMETを組み合わせて、なんとか双方向通信

  34. COMETは資源を消費する • が、Erlangなら、、、 • http://www.sics.se/~joe/apachevsyaws.html • Apache vs. Yaws • Apache が同時接続数約4千で応答なし、Yaws 同時接続数8万以上まで応答。 • http://groups.google.com/group/comp.lang.functional/msg/33b7a62afb727a4f?dmode=source • 2000万個のプロセス(64-bit erlang on a 1.5 GHz SPARC with 16 GB RAM)

  35. Erlangで作ったことは • たしかに考えやすい、作りやすい • 他の言語とものすごく変わるわけではない • スレッドのような難しさはない • 静的な型概念、型チェックがないのは痛いときもある • YAWSというプラットフォームもあることだし、Webアプリケーションには向いているのではないか • 逆に、Web的手法がErlangプログラミングに生かせることもある

  36. Erlangで作ったことは (2) • 500メッセージ/秒くらいはさばけそう • フォールトトレランスや実行時コード置き換えは今後 • 最近やっと「不要なことは書かない」「いさぎよく死ぬ」「一人で死ぬ」が分かってきた • これは、Erlangのポリシーで納得が難しい部分

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

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

  39. (おわり)

  40. サンプルの部

  41. 細かいことは気にしない。 なんとなく雰囲気がわかればよい。 後でまた説明します。 まずはサンプルとデモ

  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)].

  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.

  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.

  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. プロセス作ってメッセージ送ってみる

  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.

  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; // 問い合わせに応える } }

  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.

More Related