「一人Scala勉強会 #4」に参加してきました - Actor Model

【第四回】一人Scala勉強会の枠が余っていたので参加してきました。Scalaに興味はあるものの中々手を出せなかったのでいい機会だったと思います。
さて今日はアクターモデルがいまいち分からなかったのでPerlで実装してみることにしました。
まずアクターモデル - GIOの日記からActorでフィボナッチ数を求めるコードをPerlに移植します。

#!perl
use 5.14.0;
use strict;
use warnings;

package FibActor {
    use Mouse;
    with 'Actor::Fake';

    sub act {
        my($self) = @_;
        $self->receive(sub {
            my($self, $n) = @_;
            say $self->name, ': ', $self->fib($n);
        });
    }

    sub fib {
        my($self, $n) = @_;
        given($n) {
            when(0) {
                return 1;
            }
            when(1) {
                return 1;
            }
            defualt {
                return $self->fib($n - 1) + $self->fib($n - 2);
            }
        }
    }
}

foreach my $n(1, 5, 10, 20) {
    my $fibactor = FibActor->new(name => "fib($n)");
    $fibactor->start();
    $fibactor->send($n);
}

プログラミング言語が違うのでレシーバ($self)の明示的な使用やswitch文の違い、二項演算子!をsend()に変えているなどの違いがありますが、その他の構造はほとんど同じです。
さて、ここでActorロールが提供するものは以下のとおりです。

  • send()メソッド
  • receive()メソッド
  • start()メソッド
  • nameプロパティ

また、Actorロールを消費するクラスは以下のメソッドを実装しなければなりません。

  • act()メソッド

なのでこれらを満たすロールを定義します。

# lib/Actor/Fake.pm
package Actor::Fake;
use 5.008_001;
use Mouse::Role;

our $VERSION = '0.01';

requires 'act';

has name => (
    is       => 'rw',
    isa      => 'Str',
    required => 1,
);

has _actor_receiver => (
    is       => 'rw',
    isa      => 'CodeRef',
    writer   => 'receive',
    init_arg => undef,
);

sub start { }

sub send :method {
    my($self, @args) = @_;

    $self->act(); # should set _actor_receiver
    $self->_actor_receiver()->($self, @args);
}

no Mouse::Role;
1;
__END__

実行します。

$ perl -Ilib fib.pl
fib(1): 1
fib(5): 8
fib(10): 89
fib(20): 10946

できました。
もちろんこれはスレッド的なものは一切使わず、単に同期的・直列に実行するだけの全く意味のない実装です。実際にはバックエンドにAnyEventやCoro、threads, fork()などを使って非同期・並列に実行しなければ意味がありませんが、それは次回(?)以降に実装していくことにします。
一人Scala勉強会は初参加でしたが結構楽しめました。また参加したいですね!
See also: