Erlangの最近のブログ記事

いってきました。

 Lightweight Language Future
  http://ll.jus.or.jp/2008/program

午前中は
 - Larry Wall の基調講演と、今回のテーマ「未来」に関するパネルディスカッション。
   - LarryさんはPerl6でBNFっぽい記法をそのままかけるようになる的な話。
   - パネルはLarryさんとMatzさんが同席するという期待のパネル。(その分パネルを束ねるひとは大変だったと思います。)

昼に今回初参加の人と一緒に飯を食べた。
「100年後の定義をもうちょっとはっきりした方が議論がおもしろくなったかも」
という意見で一致した。

午後は、
- Processingとかビジュアル系の話(デモがおもしろかった。)
- フレームワークの話(ひがさんと瀧内さんが隣り合わせに座っていたのでもっと絡んでほしかった)
- コードGolf の 話、すばらしいグループプレゼンテーション。

この辺で、家から電話がかかってきて帰宅・・・。
そういえばランゲージアップデートがなかったんですね。
あれ、去年もなかったっけ・・・。

主催運営の皆さん、発表者、参加者のみなさん、お疲れ様でした。

Eralngは一度、束縛(値の代入)をした変数の値を書き換えることが出来ないようになっている。
これが軽量プロセスの効率化に寄与していることは、想像していた(Rubyの高橋さんもそうでしょうといってた)。

つっこんで考えてなかったんですけど、Erlang-MLで
「単一代入制約(single assignment restriction)のおかけでプロセス間でデータを受け渡すときに、ポインタだけ受け渡せばよいから速いんじゃないか」
ということを書いている人がいた。

あ、そっか、そういうことか。
送信元のプロセスが、変数を書き換えることがありえないから、
ロックとか考えなくてよくて、ポインタだけ渡せばいいんですね。
なるほど。

そういえば、SMPオプションをつけると遅くなる件がみかログで報告されてます。
VMwareの仮想SMPもオーバーヘッドがあるみたいで、それより
コア数分だけシングルSPUのVMを起動するほうがいいらしいっす。
ErlangもSMPオプションをつけるより、コア数分だけ実プロセスを起動したほうが
速いってことかも。

Erlang book 購入。
うぉー。読むぞー。Erlangの時代がくるぞー。
・・・っていうかちょっと読む余裕がない。

Programming Erlang: Software for a Concurrent World
Joe Armstrong
Pragmatic Bookshelf (2007/07)
売り上げランキング: 194

Erlangでとても深い話で毎回参考になる檜山さんのブログで クライアント・サーバー型分散アプリケーションの話がありました。

檜山正幸のキマイラ飼育記 - Erlang分散アプリケーションのお作法

また、バックエンドはIDLコンパイラ本体とは切り離されているので、例えばプロトコルをJSON-RPC over HTTPにして、JavaScriptのクライアント・スタブを生成する、とかもできるでしょう。

うーん、これどういうことだろう。
あれこれ悩んだすえに、gen_serverを読め、ということか。
(っていうか、最初にgen_serverの紹介だと書いてあるよ>自分)

読んでみた。
コールバックの書き方の作法はなんとなく分かった。
が、外からの呼び出し方が分からない。

そこで、JSON-RPCを使っているらしい、RBridge(Young risk taker.: [Ruby and Erlang] そろそろRBridge::Erlangについて一言いっておくか参照)ではどうしているんだろう、と思って、ソースをみてみた。

ソースの中に
- mod_jsonrpc.erl
というモジュールが組み込まれていた(あとjson.erlも)。

なになに。


start() ->
gen_server:start({local, mod_jsonrpc}, ?MODULE, [], []).

あ、なるほど。使われている。gen_server。

ということで、ここ(mod_jsonrpc.erl)を読んでけば、ちょっと理解できそうな感じがする。

Erlangで記述されたもっとも有名なアプリ ejabberd。
Windows版インストーラをとってきて
http://ejabberd.jabber.ru/download
いれてみたが、ずいぶん簡単にサーバがあがるものである。まあ、あたりまえか。

webインタフェースがあるので、アカウントなどはそこで管理できる。
データはMnesia分散データベースにはいるらしい。

- - -

Erlangを眺めていて思うのは、昔、流行った(?)「エージェント指向」が簡単にかけそうだな、ということ。まぁ、エージェント指向という言葉も実体があるようでないような感じだけれど。

1ユーザ1プロセスで管理プロセスをあげておいて、エージェント(メッセージ)を他のプロセスにポーンとなげてあげると、いろんなプロセスを渡り歩いて、結果が帰ってくる。そういう感じ。

ejabberdがErlangでかかれたのも、たぶん、1ユーザ1プロセス(ステートマシン)を実現しやすいからかな、と勝手に想像しているが、ソースをみるには至っていないので、単なる妄想である。

このひとのworkらしい。
Erlangの本もこのひとか。

Joe Armstrongさん、現在はSICS(Swedish Institute of Computer Science)の人っぽい
 Joe Armstrong SICS home page

日本のPrologの会議INAPS'96で96年に発表しているようだ。

 第2回Comet勉強会
  http://rails.office.drecom.jp/takiuchi/daily/200706/18

に行ってきました。
ドリコム瀧内さん、また参加者の方々、お疲れさまでした。
濃密な時間をいただきました。ありがとうございました。

メモです。無編集。

 * Erlangの2000万プロセスの秘密
   * 軽量プロセス(300K)の存在
   * 実行の切替戦略
     * Rubyの場合は時間で片付ける
     * ErlangはFunctionベース
       (並列的に持っているけど逐次処理)
     * UNIXにあてはめると共有リソースの問題でダメだろう
   * yaws のパフォーマンス
     * たぶんメッセージパッシングだから
     * Apacheは受信ワーカと処理ワーカが分かれている。
   * IOがからむとどうなる
     * あんまりは有効な戦略はないんじゃないか?
     * Erlangのhttpのソケットハンドリングはどうしてるのか?
       * select? sig?
       * IO系のソース読むか
       * 仮想マシンのソース?
   * Rubyのfiber
     * fiberでブロックしたらきっと止まっちゃうよね。
       * だからノンブロックで頑張ろう、と。
   * そういやRubyの1.8.5->6で変わったらしい。
   * Rubiniusは 8バイト?
     * それじゃ状態も持てない
     * 特定用途向けだとよくやるケースではあるけど。
     * Erlangはプロセス側にスタックも持っているし
       優先順位制御もしている。
       * 結局、「プロセスになにをさせたいか」と、
         オーバヘッドはトレードオフ。
   * 結局Erlangはpollじゃん
 * C10K問題
   * IO多重化 TCP select poll でわりと解決したかも
   * Producer/ConsumerパターンでQuereが溢れる
     * これは本質的だ
     * 本質的には断るしかない
       * 電話屋さんはよくやっている。
         どの程度の機器となら通信するか
   * Producer/Consumer
     * 登場人物
       * Producer : 
       * queue    : キュー
       * Consumer : 
     * ProduceしたとたんにConsume
     * AP4Rとかarmail(?)で使っている
 * Flashを使ったらCometじゃない?(Comet原理主義)
   * FlashのXMLSocketを使っていると
     L7でなんかしているサーバは困りそう
     * Proxy通るのか?
   * XMLSocketのドメイン制約
     * Level1: クロスドメインXML
     * Level2: XMLSocketサーバの実装が必要
       * System.secuurity.load.PolicyFile("xmlsocket://....")
       *  <- クロスドメインファイル
   * XMLSocket \0がセパレータ
 * ShootingStarでクロスドメイン
 * Jetty
   * Continuation
     * Runtime Exceptionを使って
   * JettyはJRE6でないとepollつかわないから、そっちがいいよ

もくじ

登録されたプロセス (Registered Processes)
  register(Alias, Pid) プロセスID: Pid のプロセスを別名(Alias)に登録

    start() ->
      Pid = spawn(num_anal, server, [])
      register(analyser, Pid).
    
    analyse(Seq) ->
      analyser ! {self(),{analyse,Seq}},
      receive
        {analysis_result,R} ->
          R
      end.

  「登録されたプロセス」へは、どのプロセスからでも
  メッセージを送ることができる。

クライアントサーバモデル (Client Server Model)
  メッセージ定義
     要求: {request, Req}
     応答: {reply, Rep}

  サーバのコード
    -module(myserver).

    server(Data) ->
      receive
        {From,{request,X}} ->
          {R, Data1} = fn(X, Data),
          From ! {myserver,{reply, R}},
            server(Data1)
      end.

  インタフェースライブラリ ( Interface Library ) = クライアント側
    -export([request/1]).

    request(Req) ->
      myserver ! {self(),{request,Req}},
        receive
          {myserver,{reply,Rep}} ->
            Rep
    end.

  ※クライアント-サーバモデルはメッセージ通信で
    簡単にかけるよ、と。「それメッセージで出来るよ。」

タイムアウト (Timeouts)
  [ time ] --("tick")--> [ B ] <--(foo)-- [A]
    receive
      foo -> Actions1;
    after
      Time -> Action2
    end
  もしメッセージ foo を 時間Timeまでに A から
  受け取っていたら Actions1を行い、そうでな
  ければActions2を行う。

タイムアウトの使いかた (Uses of Timeouts)

  sleep(T) - Tミリ秒の間、プロセスを止める
    sleep(T) ->
      receive
      after
        T ->
          true
      end.

  suspend() - プロセスを無期停止
    suspend() ->
      receive
      after
        infinity ->
          true
      end.

  alarm(T, What) - メッセージWhatはTミリ秒後にプロセスに届く
    set_alarm(T, What) ->
      spawn(timer, set, [self(),T,What]).

    set(Pid, T, Alarm) ->
      receive
      after
        T ->
          Pid ! Alarm
      end.
      receive
        Msg ->
          ... ;
      end

  flush() - メッセージバッファの吐き出し(flush)
    flush() ->
      receive
        Any ->
          flush()
      after
        0 ->
          true
      end.

   タイムアウト(after)のあとにある値0は、メッセージ
   バッファをまずチェックして、何もなければ、
   続くコードを実行する、ということを意味する。

- - -

 もくじ

Eralng Course: Module 3 - Concurrent Programming
http://www.erlang.org/course/concurrent_programming.html

もくじ

選択的メッセージ受信 (Selective Message Reception)

  以下のとき、メッセージの発信順序とは関係なく、
  foo, bar の順に受信処理される。
    recieve
      foo -> true
    end,
    recieve
      bar -> true
    end


どのメッセージも選択 (Selection of any message)
  以下の場合、変数Msgにどんなメッセージでも束縛される。
  (変数書き換えはないので)到着順で、最初のメッセージが束縛、処理される。
    receive
      Msg -> ...
    end
  ※たぶん、その後も同じ値がきたときだけ、処理されるだろう。

例: 通話 (A Telephony Example)
  AからBに電話をかける。この状態をCallというプロセスで表現

  ringing_a(A, B) ->
    receive
      {A, on_hook} ->
        A ! {stop_tone, ring},
        B ! terminate,
        idle(A);
      {B, answered} ->
        A ! {stop_tone, ring},
        switch ! {connect, A, B},
        conversation_a(A, B)
    end.

    AとBはプロセスCall内のローカル変数。
    (なので、誰と誰でも通話できる。)

    ※こんなかんじ?
       1. ringing_a(A, B)で電話をかけ、状態遷移待ち。
         Aの受話器からはダイアルトーンが流れている。
         Bの受信ベルがなっている状態。
       2. {A, on_hook}: Aが受話器を置く。
         Aのダイアルトーンを止め、Bのベル音終了、
         Aは次のイベント待ち。
       3. {B, answered}: Bが受話器をとり、応答。
         Aのダイアルトーンを止め、
         交換機にAとBの接続を指示。
         AとBの通信開始。

プロセスIDもメッセージで送れる(Pids can be sent in messages)
  プロセスA
    B ! { transfer, self() }
  プロセスB
    receive
      {transfer, A} -> C ! {transfer, A}
    end
  プロセスC
    receive
      {transfer, A} -> A ! {transfer, A}
    end

   プロセスCがプロセスAを直接呼び出している

- - -

 もくじ

Eralng Course: Module 3 - Concurrent Programming
http://www.erlang.org/course/concurrent_programming.html

もくじ

定義(Definitions)
  * プロセス Process - 並列動作。完全な仮想マシン。
                システムはたくさんの並列プロセスを
                同時に実行することがある。
  * メッセージ Message - プロセス間通信手段
  * タイムアウト Timeout - あたえられた時間の間、待機するしくみ。
  * 登録プロセス Registered Process - 名前で登録されたプロセス
  * クラ/サバ モデル Client/Server Model - 並列実行の基本モデル

プロセスの生成(Creating a New Process)
  以下の命令でプロセスが生成される。Pid2はプロセスの
  識別子(Pid = Process id)を保持する変数。
    Pid2 = spawn(Mod, Func, Args)

簡単なメッセージ送信(Simple Message Passing)
  プロセスA(送信側)
    B!{self(), foo}.
  プロセスB(受信側)
    recieve
      {From, Msg} -> Actions ...
    end
  組込関数self()は自分自身のPidを取得する。
  この場合、受信側で
    From = プロセスAのPid
    Msg = foo
  が入る
  
  プロセスA(送信側)
    B!{self(), {digits, [1, 2, 6] }.
  プロセスB(受信側)
    recieve
      {A, { digits, D } } -> analyse(D);
    end

  この場合、受信側で
    A = プロセスAのPid
    D = [1, 2, 6]
   が入るので、analyse([1, 2, 6])が実行される。

   すでに A が代入済みの場合は、データのみ
   受け入れ(accept)られる。

   ※こういうことか?
    recieve
      { _, { digits, D } } -> analyse(D).
    end

エコープロセスの例(An Echo process)
  -module(echo).
  -export([go/0, loop/0]).

  go() ->
      Pid2 = spawn(echo, loop, []),
      Pid2 ! {self(), hello},
      receive
          {Pid2, Msg} ->
              io:format("P1 ~w~n",[Msg])
      end,
      Pid2 ! stop.

  loop() ->
      receive
          {From, Msg} ->
              From ! {self(), Msg},
              loop();
          stop ->
              true
      end.

  go()で送信側。Pid2(受信側)プロセスを生成し、
  文字列メッセージを送る。Pid2からの文字列メッセージを待ち、
  受信したら Pid2に終了メッセージを送る。

  loop()は受信側。
  文字列メッセージと終了メッセージがある。
  文字列メッセージは、送信元に同じ文字列メッセージを返す。
  終了メッセージも受け取る(実行が停止する)。

  実行方法
   echo.erl に保存して、
   > c(echo).
   > echo:go().
   P1 hello
   stop
   >

- - -

もくじ

Eralng Course: Module 3 - Concurrent Programming
http://www.erlang.org/course/concurrent_programming.html

もくじ

リスト操作 ( Traversing Lists )
  よくあるパターン (このへんはProlog)

  Xを合計するsum関数の結果を、
  Xの数を出すlen関数の結果で割った
  ものが avarage ですよ。
    average(X) -> sum(X) / len(X).

  sumはリストの一番前と、その
  残り要素のsumを足した物ですよ。
  (再帰を使って総和を求める)
    sum([H|T]) -> H + sum(T);
    sum([]) -> 0.

  lenは (1 + 先頭をのぞいた
  残りのリストのlen)ですよ。
  (再帰を使って要素数を求める)  
    len([_|T]) -> 1 + len(T);
    len([]) -> 0.

  ほかにこんな再帰のパターンがよくある

    リストの各要素に2をかけたリストを返す  
    double([H|T]) -> [2*H|double(T)];
    double([]) -> [].

    第一引数のものが、第二引数のリストの
    要素に一致するならtrue
    member(H, [H|_]) -> true;
    member(H, [_|T]) -> member(H, T);
    member(_, []) -> false.

リストとアキュムレータ ( Lists and Accumulators )
  リストの平均を計算する例
    average(X) -> average(X, 0, 0).
    average([H|T], Length, Sum) ->
        average(T, Length + 1, Sum + H);
    average([], Length, Sum) ->
        Sum / Length.

    末尾再帰(tail recursive)。
    LengthとSumがアキュムレータ(加算器)の役。
    再帰しながら、引数に渡される値が足され
    ていく。

    avarage([])はラインタイムエラーになる。
    エラーハンドリングは後の章で。

Erlang起動 (Starting the system)
  erlでシェルが起動する。
  Prologとか、Tcl/tkのtclsh/wish みたいなものである。
  一行ずつ打てばそのままそれがシェル世界に反映する。
  毎行、打ち込んだ行がevalされる(Perl/JavaScript)、ということ。

    > erl
    Erlang (BEAM) emulator version 5.5.2 [source]
    [async-threads:0] [kernel-poll:false]

    Eshell V5.5.2 (abort with ^G)
    1>

  記述済みのソースファイルを取り込むには c/1 をつかう。
    1> c(hoge).
    {ok,hoge}
    2>

- - -

もくじ

Eralng Course: Module 2 - Sequential Programming
http://www.erlang.org/course/sequential_programming.html

もくじ

関数呼出(Function Calls)
  関数名はアトムのルールに沿っている必要がある
  コロン区切りでモジュール名を前置。
  引数にはErlangのあらゆるデータ構造が入る。
     module:func(Arg1, Arg2, ... Argn)
     func(Arg1, Arg2, .. Argn)

   引数の数が異なる関数は別の関数として扱われる(Prolog的)。
   なので関数を文書に表記するときは、
   somefunc/3 などと "/"のあとに引数の数をつけて示す。

関数記法 ( Function Syntax )
  まだ条件があるときは ";" 、最後の条件は "." で終端する。
  評価は上から順に行われる(バックトラックしない)。
  ボディ部も前から順に呼び出される。
    func(Pattern1, Pattern2, ...) ->
        ... ;
    func(Pattern1, Pattern2, ...) ->
        ... ;
        ...
    func(Pattern1, Pattern2, ...) ->
        ... .
  ※引数の数が違う同名の定義は、別関数と認識される。

ガードされた関数 (Guarded Function Clauses)
 whenを使って関数に条件式をつけられる(ガード)
 変数は全て束縛済み(代入済み)である必要がある
  factorial(0) -> 1;
  factorial(N) when N > 0 ->
    N * factorial(N - 1).
 完全にガードすると、関数定義は順番に依存しなくなる
  factorial(N) when N > 0 ->
    N * factorial(N - 1);
  factorial(0) -> 1.
  ※上は、第1の例と同じ意味で、下の例とは違う。
  factorial(N) ->
    N * factorial(N - 1);
  factorial(0) -> 1.
 
モジュール システム(Module System)
  -moduleでモジュール名定義。
  -exportで関数公開。
    -module(demo).
    -export([double/1]).
    double(X) ->
        times(X, 2).
    times(X, N) ->
        X * N.
    ※この場合、double/1 はモジュール外から
      アクセスできるが、times/2 はだめ

ビルトイン関数 (Built In Functions / BIFs)
  処理系に入っている関数。詳細はマニュアル嫁。
    date()
    time()
    length([1,2,3,4,5])
    size({a,b,c})
    atom_to_list(an_atom)
    list_to_tuple([1,2,3,4])
    integer_to_list(2234)
    tuple_to_list({})

ガードの例 (Examples of Guards)
  number(X) - Xは数字(Number
  integer(X) - Xは整数
  float(X)   - Xは浮動小数点数
  atom(X)   - Xはアトム
  tuple(X)   - Xはタプル
  list(X)    - Xはリスト
  length(X) == 3  - リストXの要素数は3
  size(X) == 2 - タプルXのサイズは2
  X > Y + Z  - X > Y + Z
  X == Y  - X は Y と等しい
  X =:= Y  - X は Y と厳密に等しい
  ※ 1 == 1.0 は成功、 1=:=1.0 は失敗

- - -

 もくじ

Eralng Course: Module 2 - Sequential Programming
http://www.erlang.org/course/sequential_programming.html

もくじ

数値(Numbers)
  普通に数値を書く
    2#1010, 16#1AD3: 数字+シャープで2進16進。
    $A: $マークは Char->Ascii
    123.45: 小数点をうつと float
    123E-10: 有効数字のパターンでもかけるよ

アトム(Atoms)
  普通に文字を書く
    aaaa : "aaaa"という文字
    'I am' : スペースをいれたいときはシングルクォート

タプル(Tuples)
  固定長配列
    { aaa, bbb, ccc } : 要素数3のタプル

リスト(Lists)
  可変長配列
    [ aaa, bbb, ccc ] : 要素数3のリスト

変数(Variables)
  大文字で始める。
     値の割り当て(変数束縛)は一回だけ。
     上書きはできない。

複雑なデータ構造 ( Complex Data Structures )
  タプル/リストは重ねられる。

パターンマッチ(Pattern Matching)
  タプル/リストでも変数束縛
    A = 10             : A:=10
    {B, C, D} = {10, foo, bar}  : B:=10, C:=foo, D:=bar
    {A, A, B} = {abc, abc, foo} : A := abc, B = :foo
                      ※Aは2回あるが値が同じなので無矛盾
    {A, A, B} = {abc, def, 123}  : 失敗する
                       ※Aは2回あり、値が違うので
    [A,B,C] = [1,2,3]        : A:=1, B:=2, C:=3

    [A,B,C,D] = [1,2,3]       : 失敗する
                       ※要素数が違う。GNU Rとはちがう。

パターンマッチ(Pattern Matching)
  "|"をつかったリストの操作
    [A,B|C] = [1,2,3,4,5,6,7]   : A:=1, B:=2, C:=[3,4,5,6,7]
    [H|T] = [1,2,3,4]        : H := 1, T := [2,3,4]
    [H|T] = [abc]          : H := abc, T := []
    [H|T] = []            : 失敗
             ※ HはHead、TはTail。先頭の値を取り出す。
               foreach/for...inに相当する処理でよくつかう。
               Perlだと pop()/unshift()。
               最後は空リスト[]になり失敗する。
    {A,_, [B|_],{B}} = {abc,23,[22,x],{22}} : A:=abc, B:=22
             ※ _ は無名変数。参照不可。何度でも使える。
               普通の変数は一度しか代入できないので、
               使い捨てしずらい。
               なので、いらんものは _ に放りこんでおく。

- - -

 もくじ

Eralng Course: Module 2 - Sequential Programming
http://www.erlang.org/course/sequential_programming.html

もくじ

* 基礎部分に、並列性とエラー回復能力 を持っていなければならない
* 実行モデルにはバックトラッキングは必須ではない (Lisp/Prolog にない特性)
* 並列性の粒度 - 1非同期通信=1言語内プロセス (Parlog にない特性)
* そこで、Lisp/Prolog/Parlog の望ましい特徴を持ち、並列性と持ちあわせた新言語を設計した。

- - -

 もくじ

Eralng Cource: Module 1 - History
http://www.erlang.org/course/history.html

1. Prologに似てるなあ。
2. 大学のとき、SICStus Prolog という処理系を使っていたのをおもいだすなぁ。PrologとTcl/Tkでソケット通信しながらゴニャゴニャしたなぁ。どっちもソケットは簡単に書けたなぁ。
3. SICStus Prologはスウェーデンの研究所が作った奴だったなぁ。Erlangもスウェーデンだから、きっとそういうコミュニティがあるんだろうなぁ。

- - -

SICStus Prologは健在みたいです。

SICStus Prolog Homepage

「Javaに並列処理と関数型言語の要素を」、ティム・ブレイ氏 − @IT

 「Hadoopは外部ライブラリですが、Java言語自体で並列処理をサポートすることも必要」(ブレイ氏)といい、そのモデルとして2年ほど前から“Erlang”(アーラン)に注目しているという(参考記事:twitterブームの陰で注目を集める“Erlang”)。「Erlangはエリクソンが開発した古い言語ですが、とても興味深い特徴を持っています。きわめて効率的にプログラムの並列化ができ、ふつうのAMDやインテルのCPUを使って25万スレッドを動かすこともできます。ただErlangはオブジェクト指向言語ではなく、関数型言語です。つまり、一般的な言語になじんだ、ほとんどのプログラマには奇異に感じられるので、われわれとしてはErlangが持っているいいところを、もっと身近なRubyやJavaに入れていこうと考えています」。Java上で動くRubyの実装であるJRubyが、本家Rubyより先にこうした並列処理サポートを取り込む可能性もあるという。

Java上にJRubyと軽量プロセスが載ってきたりするのでしょうか。

ということにまず感動。
話題の道具は、すでに使える状態でそこにあった。

2010年11月

  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        

アーカイブ