camel

PerlにLambda Calculusが入っているというのは以前何度か紹介した通りだが、実はMonadも入っているのである。そして、皆さんも知らない間に使っているのである。

檜山正幸のキマイラ飼育記 - 世界で一番か二番くらいにやさしい「モナド入門」
「なら、予備知識ゼロでモナドの説明をしてやろうじゃねーか」と。

それでは、世界で一番Monadが簡単に使える言語(0番はPerl6!)での実例をいくつかお見せする。

まずは一番簡単な例から。

use strict;
use warnings;
package Tie::Verbose;
use Tie::Scalar;
use base 'Tie::StdScalar';
sub TIESCALAR{
    my $pkg    = shift;
    my $scalar = shift;
    bless \$scalar, $pkg;
}
sub STORE {
    my $self  = shift;
    my $value = shift;
    $$self = $value;
    print "value of $self = $value\n";
    $self;
}
1;
package main;
my $m;
sub fact{
    my $n = shift;
    $m = $n <= 1 ? 1 : $n * fact($n-1);
}
fact(10);
tie $m, 'Tie::Verbose' # $t is now monad!
fact(10);              # now see what happens!       

最初のfact()では何も表示されないが、次のtieしたとたんに、次のfact()では代入される都度その値が出力される。計算の結果はtieしなかった時とまるで変わらない、すなわち参照透過であるが、値が出力されるという副作用がきちんと発生している。

GDBMやBerkeley DBへのtieというのは、日頃普通に使っていると思うが、これも「DBMに保存されるという副作用を持つMonad」と解釈すればいいのだ。Perlのtieは、基本的にはMonadなのである。

結局Monadを実装するためには、値と副作用を別々に保持し、値に関しては参照等価性を保ちさえすればいい。実はこれはOOでやった方が、ずっと楽である。それを今からお見せする。

#
use strict;
use warnings;
package Monad::Maybe;
use overload
    q("") => sub { $_[0]->{value} },
    fallback => 1;
sub new{
    my $pkg   = shift;
    my $value = shift;
    bless { value => $value, state => undef }, $pkg;
}
sub Maybe  { __PACKAGE__->new(@_) }
sub Monadic{
    my $coderef = shift;
    sub {
        my $op0 = shift;
        my $op1 = shift;
        return $op0 unless $op0->{value}; # Nothing
        no warnings;
        $op0->{value} = eval{
            $coderef->($op0->{value}, $op1->{value});
        };
        if ($@){
            $op0->{state} = $@;
            $op0->{value} = undef;
        }
        $op0;
    }
}
# package main;
my $mdiv = Monadic(sub{ $_[0] / $_[1] });
no warnings; 'unintialized';
print $mdiv->(Maybe(1), Maybe(1)), "\n";
print $mdiv->(Maybe(1), Maybe(0)), "\n";
print $mdiv->(Maybe(1), Maybe(2)), "\n";
print $mdiv->($mdiv->($mdiv->(Maybe(1), Maybe(2)), Maybe(0)), Maybe(1)), "\n";

これは、見ての通り、HaskellのMaybeを実装したものである。printの2行目と4行目に注目して欲しい。0で割っているのに、演算はこともなげに進んでいる。そしてobjectであるにも関わらず、overloadのおかげであたかも普通の数値のように出力されている。

こうして見れば、Monadが恐るるに足りないことは分かるだろう。中級以上のPerl Userは、実はすでにMonadを知っているのである。別の名前で呼んでいるだけで。

もちろん関数的にMonadを実装するのもそれほど難しくない。以下にそのものずばりのLinkを張っておく。もっとも見てのとおり、この人のPerl Codeはあまり流暢ではないけど、意味は充分汲み取れるはずだ。

もっとも、モナド則を満たしたreturn (monad constructor)と>>= (bind operator) がないと、Monadしている気になれないという人もいるだろう。実は今Monad::シリーズのModuleを片手間にこさえているところだ。きりのいいところで公開することになるだろう。あまり期待しないでお待ちいただきたい。

Dan the Monadic Perl Monger