From POE to Erlang

A Perl programmer's journey into concurrency-oriented programming

(Perlプログラマによる並行指向プログラミング)

Ever needed to write a concurrent program in Perl?

(Perlで並行プログラムを書かなきゃいけなくなったことはありますか?)

As usual, you have lots and lots of options...
(例によって選択肢はたくさんあります)

Usually something else (the OS) helps out
(ふつうはほかのものが助けてくれます)

Best example is Apache: multiple Perl processes
(Apacheがなによりの例でしょう)

Perl is a GLUE language: We leave the messy bits to others, and glue the finished parts
(Perlはのり付けをするだけ。面倒くさい部分はほかの人にまかせておけばいい)

Concurrency and Perl

並行処理とPerl

The Perl5 life: Thread worry-free

Perl5ライフ: スレッドなんて気にしない

The Perl5 life: Thread worry-free

Perl5ライフ: スレッドなんて気にしない

For example...

(たとえば)

in other words...

(いいかえると)

Hmm...

Select Loops are difficult, but there are frameworks

(selectループはむずかしいけど、フレームワークはある)

POE is...

From http://poe.perl.org
POEtry ... Panel Of Experts ... Parallel Object Executor ... Parcel Out Execution ... Parenthetically Over-Engineered ... Parity Of Evil ... Part Of Elephant ... Particles Of Eternity ... Party On, Ebenezer ... Passed Out from Excitement ... Pathetically Over-Engineered ... Peace On Earth ... Penes Over-Emphasized ... Perfect Orange Eater ... Perfectly Oblique Egg-plant ... Periodically Orbits Earth ... Perl Obfuscation Engine ... Perl Object Environment ... Perl: Objectively Excellent ... Perl Objects for Enterprises ... Perl Objects for Events ... Perl On Extasy ... Perl Operating Environment ... Perl Operator Extravaganza ... Perl Over Easy ... Perl Over Ethernet ... Perl Overdrive Engine ... Perlmud Offers Expansions ... Perpetual Orgone Energy ... Persistent Object Environment ... Persnickity Oblong Erudition ... Phallic Overture Ejaculate ... Philanthropic Organization Enterprises ... Physician Order Entry ... Piece Of Eight ... Pigs, Owls, and Elephants ... Piles Of Eugh ... Pious Object Excelsior ... Plain Old English ...

POE Comes with:

(POEについてくるもの)

Silly Example

(くだらない例)

We have a good friend who makes us sandwiches
(私たちにはサンドイッチをつくってくれる友人がいます)

We can eat as we ask her to keep making sandwiches for us
(サンドイッチをつくってほしいとお願いすれば食べさせてくれますが)

... till she gets bugged with us..
(度が過ぎると怒り出します)

Silly Sandwiches

(くだらないサンドイッチ)

A recursive loop, POE-ish
(POE風の再帰ループ)

sub tick_tock {
    my ($me) = $_[KERNEL]->alias_list();
    my $now = time();
    print "$me : $now\n";
    $_[KERNEL]->delay('tick_tock', 1);
}
my @types = qw( 
    cheese
    egg
    BLT
    chicken
    chicken_and_cheese
    tomato_and_cucumber
    jelly
    chocolate_cream
);

Silly Sandwiches

(くだらないサンドイッチ)

POE::Session->create(
    inline_states => {
        _start => sub {
            my ($heap, $kernel) = @_[HEAP, KERNEL];
            $kernel->alias_set('friend_in_need');
            $heap->{i} = 0;
            print "*** I can make you a sandwich!\n";
            $kernel->yield('tick_tock');
        },
        make_sandwich=> sub {
            my ($kernel, $heap, $type) = @_[KERNEL, HEAP, ARG0];
            $heap->{i}++;
            my $i = $heap->{i};
            if ($i < 4) {
                # make a sandwich
                print "*** Making a $type sandwich for you.\n";
                $kernel->post(faiz => sandwich_ready => $type);
            } else {
                $kernel->yield('fed_up');
            }

            if ($i > 4) {
                $kernel->post(faiz => 'i_hate_you');
            }
        },
        fed_up => sub {
            print "*** go make your own sandwiches.\n";
        },
        tick_tock => \&tick_tock,

    },
);

Silly Sandwiches

(くだらないサンドイッチ)

POE::Session->create(
    inline_states => {
        _start => sub {
            my $kernel = $_[KERNEL];
            $kernel->alias_set('faiz');
            $kernel->yield('tick_tock');
            $kernel->yield('beg');
        },
        beg => sub {
            my $i_want_to_eat = shift @types;

            unless ($i_want_to_eat) {
                $_[KERNEL]->yield('time_for_a_drink');
                return;
            }

            print "--> I feel like a $i_want_to_eat sandwich.\n";

            $_[KERNEL]->post(
                friend_in_need => make_sandwich =>
                $i_want_to_eat
            );
            $_[KERNEL]->delay('beg', 0.5);
        },
        sandwich_ready => sub {
            my ($sandwich) = $_[ARG0];
            print "--> yummy $sandwich sandwich (burp)\n";
        },
        time_for_a_drink => sub {
            print "--> Faiz: thirsty now.\n";
        },
        tick_tock => \&tick_tock,
    }
);
$poe_kernel->run();

GUI Programming

(GUIプログラミング)

Can be yucky.

(ひどい目にあうこともある)

Examples...

(たとえば)

# We want to do this
while (sleep(100)) {
    my $response = $ua->request($request);
    # waiting....
    my $data_to_display = $self->parse($response);
}

# WHILE we also do this....
Gtk2->main;

# Or While we do this:
MainLoop(); 

Perl/GTK+ and POE

use Gtk2 -init;
use POE::Kernel { loop => "Glib" };
use POE::Session;

POE::Session->create(
    inline_states => {
        _start   => sub {
            $_[KERNEL]->post('fetch');
        },
        fetch => sub {
            my $request = ...; # Create a HTTP::Request
            $_[KERNEL]->post(ua => request => response => $request);
        },
        response => sub {
            my ($req_packet, $resp_packet) = @_[ARG0, ARG1];
            my $response = $resp_packet->[0];
            my $data_to_display = parse($response);
            $_[HEAP]->{widget}->setText($data_to_display);
            $_[KERNEL]->delay(fetch => 100);
        }
    }
);
$poe_kernel->run();


What do the Gtk-Perl guys say about threads?

(Gtk-Perlの人たちはスレッドのことをどう思っているんだろう)

Can I use threads with Gtk2-Perl?

(Gtk2-Perlでスレッドって使えるの?)

Yes, if you are very careful. (気をつければ使える)

In general, you want to mess with the gui from only one thread, and then you're safe. An extra wrinkle for gtk2-perl is that you need to create your extra threads before you create any gtk2-perl objects or widgets.

(ふつうGUIをいじる場合はひとつのスレッドのみからにするものですし、そうしている分には問題ないですが、gtk2-perlの場合、オブジェクトやウィジェットを作る前にかならず余分のスレッドをつくっておく必要があります)

There are a few threads in the mailing list archives about this topic. This message, in particular, includes working code: http://mail.gnome.org/archives/gtk-perl-list/2003-November/msg00028.html

(メーリングリストにもいくつかスレッドがあります。動作するコードも紹介されています)

What do the Gtk-Perl guys say about threads? (continued)

(Gtk-Perlの人たちはスレッドのことをどう思っているんだろう)

Because of this mess, it's usually best to avoid threads if you can. If you need to monitor a file or socket for input, use Glib::IO->add_watch to wait for events on that file descriptor. You can also spawn other programs and communicate via pipes, and even use Gtk2::Plugs and Gtk2::Sockets to put that child process' widgets into the parent's gui.

(このような問題があるのでふつうはスレッドを避けた方がよいですが、 ファイルやソケットを監視する場合はGlib::IO->add_watchを使ってイベント待ちをしてください。 ほかのプログラムを実行したり、パイプ越しにやりとりすることもできますし、 Gtk2::PlugsやGtk2::Socketsを使えば子プロセスのウィジェットを親のGUIに入れることもできます)

POE is a Framework

(POEはフレームワーク)

Data Marshalling

(データマーシャリング)

POE::Component::Server::TCP->new
  ( Alias => "myserver",
    Port => 7777,
    ClientInput => sub {
        my ( $session, $heap, $input ) = @_[ SESSION, HEAP, ARG0 ];
        print "Session ", $session->ID(), " got: $input\n";
        $heap->{client}->put( "you say $input." }; 
      },
    ClientFilter => "POE::Filter::Line",
  );

Protocol Stacks

(プロトコルスタック)

POE::Component::Server::TCP->new
  ( Alias => "myserver",
    Port => 7777,
    ClientInput => sub {
        my ( $session, $heap, $input ) = @_[ SESSION, HEAP, ARG0 ];
        print "Session ", $session->ID(), " got: $input\n";
        $heap->{client}->put( "you say $input." }; 
      },
    ClientFilter => new POE::Filter::Stackable(
                Filters => [ 
                    "POE::Filter::Zlib", 
                    "POE::Filter::Line", 
                ]);
  );

Concurrency without Locks

ロックのない並行処理

'flavors' and variations of concurrency we hear of 私たちが並行処理といって思い浮かべるのは

Moore's Law - Remember?

(ムーアの法則 - 覚えていますか)

"The number of transistors on a chip will double every two years."

(チップ上のトランジスタの数は2年ごとに倍になる)

In the 1990's, Moore's Law was interpreted as the doubling of processor power every one and a half years (18 months).

(1990年代にはプロセッサの能力が1年半で倍になると解釈されていました)

So, has Moore's Law Failed?

(これは間違っていたのでしょうか?)

Is Moore's Law Dead?

(ムーアの法則は死んだのか)

Is the free lunch over?

(フリーランチは終わった?)

Never mind Moore's Law

(ムーアの法則はどうでもいいけど)

The point is, that processor speeds haven't been increasing too much lately.

(最近プロセッサの「速さ」は速くなってない)

The multi-core future (present?)

(マルチコアの将来)

better, but not faster

(性能はあがったけど、速くはなっていない)

if not faster, then what?

(速さでなければ、どこがよくなったのか)

The Multicore future

(マルチコアの将来)

Unless you're satisfied with nothing more than just the OS benefiting

(OSが恩恵を受ければ満足という人以外は)

Models for Concurrency

(並行モデル)

Historically, there have been two main theoretical models for concurrency

(歴史的に大事な理論は2つ)

  1. Shared State Concurrency (共有ステート並行処理)
    • Dijkstra (ダイクストラ)
    • The Dining Philosophers (「食事をする哲学者」問題)
    • Pessimistic Locking does not scale (悲観ロックはスケールしない)
    • Solutions exist, but in real life, locking requires great care and is error-prone (解決策はあるが、現実問題としてロックには気をつかうしエラーのもとにもなりやすい)
  2. Message Passing Concurrency (メッセージ渡し並行処理)
    • The Actor Model (アクターモデル)
    • Share-nothing, just pass messages (共有せずメッセージを渡すのみ)
    • Asynchronous (非同期)
    • Sometimes models the real world better (モデルの方がよい場合もある)

Erlang: From Telecom-Specific to General Purpose

(Erlang: 通信業界御用達から一般用に)

Erlang: From Telecom-Specific to General Purpose

(Erlang: 通信業界御用達から一般用に)

The Erlang Programming Language

(Erlangという言語の特徴)

The Erlang Programming Language

(Erlangという言語の特徴)

Erlang: Variables, Atoms, Primitive datatypes

(Erlang: 変数、アトム、プリミティブデータ型)

Erlang: Data Structures

データ構造

Erlang: Cons The Magnificent

(Erlang: 偉大なるCons)

List Operations are both Lispy and Prologesque

(リスト操作はLisp風味でありProlog風味)

Erlang: Pattern Matching

Used with pattern matching to implement a simple function to double all numbers in a list:

(パターンマッチとともにリスト内のすべての数を2倍する単純な関数を実装)

double([H|T]) -> [2*H|double(T)];
double([])    -> []. 
> double(List).
[2,4,6,8,10,12]

Erlang: Variables that don't 'vary'

(Erlang: 変化しない変数)

Single Assignment (as in Haskell)

(単一割り当て)

Erlang: Functional, Dynamic

(Erlang: 関数的、動的)

Lambda is fun!

(ラムダはおもしろい)

Erlangfun (X) -> X+1 end.
Javascriptfunction (x) { return x+1 }
Perlsub { my $x = shift; $x+1; }
Lisp/Scheme(lambda (x) (1+ x))
Haskell\x -> x + 1
Rubylambda {|x| x+1 }

Erlang: Functional Programming Language

(Erlang: 関数型プログラミング言語)

Erlang: List Comprehensions

(リスト内包表現)

> [X || X <- [1,a,2,s,3,d,4,f,5,g,6,h], integer(X), X rem 2 == 0].
[2,4,6]
> [{X, X-1} || X <- [1,a,2,s,3,d,4,f,5,g,6,h], integer(X), X rem 2 == 0].
[{2,1},{4,3},{6,5}] 
Here's a Quicksort Implementation: (クイックソートの実装例です)
qsort([]) -> [];
qsort([Pivot|T]) -> 
    Left = [X || X <- T, X < Pivot],
    Right = [Y || Y <- T, Y >= Pivot],
    qsort(Left) ++ [Pivot] ++ qsort(Right).  
Of course, this code is not the most efficient (the ++ is apparently slow, etc). But it does show list comprehensions in action. (もちろんこのコードはもっとも効率のよいものではありませんが、 リスト内包表現の実例にはなるでしょう)

Erlang: Quicksort

qsort([]) -> [];
qsort([Pivot|T]) -> 
    Left = [X || X <- T, X < Pivot],
    Right = [Y || Y <- T, Y >= Pivot],
    qsort(Left) ++ [Pivot] ++ qsort(Right).  
The above code could be read as: (上のコードはこのように読めます)

"qsort of an empty list is an empty list."
"qsort" of a list is the qsort of the left sublist (of items smaller than the pivot), appended to the pivot, and then the qsort of the right sublist (of larger items),
where the left and right sublists are defined by the list comprehensions;
using the first element in the list as the Pivot.

(空のリストのqsortは空のリスト)

(リストのqsortは、左の(ピボットより小さな要素の)サブリストのqsortをして、そのあとにピボットと、右の(ピボットより大きな要素の)サブリストのqsortを続けるというもの。こで左右のサブリストはリスト内包表現で定義し、リストの最初の要素をピボットとしています)

Erlang: More List Comprehensions (couldn't resist)

(Erlang: もっとリスト内包表現)

Pythagorean Triplets

(ピタゴラス数)

Remember Pythagoras' Theorem? (ピタゴラスの定理を覚えていますか)

Wikipedia: A Pythagorean triple consists of three positive integers a, b, and c, such that a2 + b2 = c2. (ピタゴラス数は次の関係を満たす3つの自然数からなります)

Generate them using list comprehensions? (これをリスト内包表現を使って生成してみましょう)

pyth(N) ->
    Seq = lists:seq(1,N),	%% In Perl, @seq = (1..$n); # ;-)
    [ {A,B,C} ||
        A <- Seq,
        B <- Seq,
        C <- Seq,
        A+B+C =< N,
        A*A+B*B == C*C 
    ].  

Pythagorean Triplets

(ピタゴラス数)

> pyth(50).
[{3,4,5},
 {4,3,5},
 {5,12,13},
 {6,8,10},
 {8,6,10},
 {8,15,17},
 {9,12,15},
 {12,5,13},
 {12,9,15},
 {12,16,20},
 {15,8,17},
 {16,12,20}]

Building Servers is Intuitive

(サーバをつくってみると勉強になります)

A Parallel server 並列サーバ

start(Port) ->
    {ok, LSock} = gen_tcp:listen(Port, [binary, {reuseaddr, true},
                                       {packet, 4},
                                       {active, true}]),
    spawn(fun() -> par_connect(LSock) end).

par_connect(LSock) ->
    {ok, Socket} = gen_tcp:accept(LSock),
    spawn(fun() -> par_connect(LSock) end),
    loop(Socket).

loop(Sock) ->
    receive
        {tcp, Sock, Bin} ->
            io:format("Got: ~p~n", [Bin]),
            Reply = Bin,
            gen_tcp:send(Sock, term_to_binary(Reply)),
            loop(Sock);
        {tcp_closed, Sock} ->
            io:format("Server socket closed.~n")
    end.

Digression into Real-time stuff

(リアルタイムな余談)

Mouse Events

(マウスイベント)

Double-click Problem

(ダブルクリック問題)

Double-Click Problem: Perl/GTK+

(ダブルクリック問題: Perl/GTK+)

my $evt_box = Gtk2::EventBox->new;
$evt_box->signal_connect(button_press_event => sub {
    my ($widget, $event) = @_;

    if ($event->type eq 'button-press') {
        print "single click...\n"; 
    } elsif ($event->type eq '2button-press') {
        print "double click...\n"
    }
});

Double-Click Problem: Perl/GTK+, POE

(ダブルクリック問題: Perl/GTK+, POE)

POE::Session->create(
    inline_states => {
        _start   => \&ui_start,
        evt_pressed => \&handle_pressed,
        evt_single_click => sub {
            print "single click!\n";
        },
        evt_double_click => sub {
            print "double click!\n";
        }
      }
);

sub handle_pressed {
    my ( $kernel, $session, $heap ) = @_[ KERNEL, SESSION, HEAP ];
    my $event = $_[ARG1]->[1];
    $kernel->alarm_remove(delete $heap->{single_click_alarm});
    if ($event->type eq 'button-press') {
        $heap->{single_click_alarm} = $kernel->delay_set(evt_single_click => 0.2 );
    } elsif ($event->type eq '2button-press') {
        $kernel->yield('evt_double_click');
    }
}

In Erlang

get_event() ->
    receive
         {mouse, click} ->
             receive
                 {mouse, click} ->
                     double_click
             after double_click_interval() ->
                 single_click
             end
         ...
    end.

end.

(おわりに)