Pixel Pedals of Tomakomai

北海道苫小牧市出身の初老の日常

Data::Monad::CondVar をリリースしました

YAPC Asia 2011で話をする予定のモジュールをCPANへアップしました。

Data::Monad::CondVarJSDeferred みたいなもんです。

use AnyEvent;
use AnyEvent::HTTP;

sub random_choice {
    my ($data, $headers, $cb) = @_;
    my (@url) = $data =~ m{href="(http:[^"]+)}g;

    http_get +(shuffle @url)[0], $cb;
}

my $cv = AE::cv;
http_get "http://yapcasia.org/2011/", sub {
    random_choice @_ => sub {
        random_choice @_ => sub {
            random_choice @_ => sub {
                my ($data, $headers) = @_;
                my ($title) = $data =~ qr{<title>([^<]+)};
                print $title, "\n";
                $cv->send;
            };
        };
    };
};
$cv->recv;

のようにネストが深くなりがちの非同期処理を、

use AnyEvent;
use AnyEvent::HTTP;
use Data::Monad::CondVar;

sub random_choice {
    my ($data, $headers) = @_;
    my (@url) = $data =~ m{href="(http:[^"]+)}g;

    as_cv { http_get +(shuffle @url)[0], $_[0] };
}

as_cv { http_get "http://yapcasia.org/2011/", $_[0] }
->flat_map(\&random_choice)
->flat_map(\&random_choice)
->flat_map(\&random_choice)
->map(sub {
    my ($data, $headers) = @_;
    my ($title) = $data =~ qr{<title>([^<]+)};
    print $title, "\n";
})->recv;

のようにネストせずに書けるようにするものです。

Data::Monad はData::Monad::CondVar のモナドの実装部分を抽出したもので、Listモナドとかが実装されてます。

use Data::Monad::List;
use Data::Monad::Base::Sugar;

my $result = Data::Monad::Base::Sugar::for {
    pick \my $x => sub { scalar_list 1 .. 100 };
    pick \my $y => sub { scalar_list $x .. 100 };
    pick \my $z => sub { scalar_list $y .. ($x + $y > 100 ? 100 : $x + $y) };
    satisfy { $x**2 + $y**2 == $z**2 };
    yield { $x, $y, $z }
};

print join(',', @$_), "\n" for $result->values;
# 3,4,5
# 5,12,13
# ...
# 60,80,100
# 65,72,97

モナドとして実装したいものがあれば、Data::Monad::Base::Monad を継承して作ると、map*1やflatten*2、sequence 辺りのメソッドが自動生成されたり、for構文が使えたりします。

StateモナドやらContモナドやらは、Monad Transformerまで定義するかで迷ってるのでまだ入れてないです。

詳細はトークの中で。

*1:Haskellではfmap

*2:Haskellではjoin