Maybeモナドの実装です。
簡単な解説
Maybeモナドの関手のT_objectは、渡された集合に対して、すべてをリファレンス化した集合にundefを加えたものに移します。こうすることによって、元々持っていた値を全て表現すると同時に新しいundefと言う値を手に入れられます。undefがHaskellのNothingとなります。
実装
リファレンスをとる処理がうざかったので、ヘルパー関数にまとめました。こいつの名前をjustにしたので、Haskellっぽくなりました*1。
sub just { my $value = shift; return \$value; } my $list_monad = Monad->new( T_arrow => sub { my $arrow = shift; # A -> B return sub { my $tx = shift; # TA -> TB defined $tx or return undef; return just $arrow->($$tx); }; }, eta => sub { return just shift; }, mu => sub { my $ttx = shift; defined $ttx or return undef; return $$ttx; }, );
使い道
途中で失敗する可能性のある処理を繋げるのに便利です。まともに書くと長くなるので、do_ と言うショートカットを準備します。こいつは関数をflat (A->TA を TA -> TAに) にして、逆順で合成してくれます。(flat g) . (flat f) = do_ f, g のイメージです。
sub do_ { my @f = @_; my $composite; while( my $f = shift @f ){ $f = $maybe_monad->flat->( $f ); if( $composite ){ $composite = comp $f, $composite; }else{ $composite = $f; } } return $composite; }
do_ があれば、こんな感じで書けます。
sub get_number { shift =~ qr{(<span.+?>.+?</span>)} or return undef; return just $1; } sub cut_tag{ my $html = shift; $html =~ s|<[^>]+>||g; return $html; } sub parse_number { shift =~ qr{^(\d+)/(\d+)$} or return undef; return just [$1, $2]; } sub div{ my ($n1, $n2) = @{ $_[0] }; return undef if $n2 == 0; return just $n1 / $n2; } my $parse_rate = do_ \&get_number, ( comp $maybe_monad->eta, \&cut_tag ), \&parse_number, \÷ print Dumper $parse_rate->(just <<__HTML__); <span id="number">3/10</span> __HTML__ print Dumper $parse_rate->(just <<__HTML__); HOGEHOGE __HTML__ print Dumper $parse_rate->(just <<__HTML__); <span id="number">not a number</span> __HTML__ print Dumper $parse_rate->(just <<__HTML__); <span id="number">100/0</span> __HTML__ __END__ 【結果】 $VAR1 = \'0.3'; $VAR1 = undef; $VAR1 = undef; $VAR1 = undef;
HTMLのパースや数値のパース、割り算と3カ所失敗所があるにもかかわらず、条件分岐せずにそのままつなげることができてしまいます。もうちょっとシンタックスシュガーを加えれば、すごく便利な機能になりそうです。なお、cut_tagだけは関手で移される前の世界の関数なので、etaを使ってMaybeモナドの世界に引きずりこんでます。
感想
Listモナドの時のテストは、関手を施した先の値(リスト or just)を使う関数と値を書き換えるだけで、実はそのまま使えてしまいます(diff参照)。それなのに、Listモナドと全然振る舞い方が違うのが、モナドの表現力の高さを物語ってます。
33c40 < my $v = [ {key1 => 1, key2 => 2}, {key1 => 3, key2 => 4}, ]; --- > my $v = just 123; 49c56 < my $v = ['abc', 'defg', 'hijklm']; --- > my $v = just "abc"; 78c85 < my $v = [[ 'perl', 'python', 'haskell' ]]; --- > my $v = just just 'maybe'; 95c102 < my $v = [ [ [1,2] ], [ [3, 4], [5, 6] ] ]; --- > my $v = just just just [1,2,3]; 112c119 < my $v = [1, 2, 3]; --- > my $v = just {key => 100}; 127c134 < my $v = [2,3,4]; --- > my $v = just "what"; 137c144 < my $f = sub { [ ( 0 ) x $_[0] ] }; --- > my $f = sub { $_[0] != 0 ? just 100 / $_[0] : undef }; 150,151c157,158 < my $f = sub { [ split / /, shift ] }; < my $g = sub { [ split /,/, shift ] }; --- > my $f = sub { $_[0] > 0 ? just 100 / $_[0] : undef }; > my $g = sub { just 50 + $_[0] }; 155c162 < my $v = ["ABC DEF", "GH, IJK", "LM N,OP"]; --- > my $v = just 10;