• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Monads in perl
 

Monads in perl

on

  • 3,488 views

The slide shows what monads are and how I implement it in Perl.

The slide shows what monads are and how I implement it in Perl.

Statistics

Views

Total Views
3,488
Views on SlideShare
3,206
Embed Views
282

Actions

Likes
4
Downloads
12
Comments
0

8 Embeds 282

http://yapcasia.org 257
https://twitter.com 12
http://paper.li 8
http://a0.twimg.com 1
http://webcache.googleusercontent.com 1
https://si0.twimg.com 1
https://web.tweetdeck.com 1
http://www.slashdocs.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Monads in perl Monads in perl Presentation Transcript

    • Monads in Perlhiratara <hira.tara@gmail.com>
    • About med.hatena.ne.jp/hirataratwitter.com/hirataraworking as a Perl programmera fan of Mathematicsa reporter of this YAPC
    • What are Monads?
    • Actually, monads arenot anything special.
    • Category theory helps your understanding.
    • An arrow is a function split Regex, Str, Int [Str] @str = split /$regex/, $str, $n lengthStr Int$len = length $str
    • Or represents a method newClassName CGI $cgi = CGI->new param CGI, Str [Str] @values = $cgi->param($str)
    • Connecting two arrows * f g* *
    • Connecting two arrows * f g f;g * * sub f;g {sub f;g { g(f(@_)) } or my @v = f(@_); g(@v) }
    • The 2 laws of arrows
    • 1) Identity sub id { @_ } f;idid f id id;f
    • 2) Associativity f;(g;h)f g h (f;g);h
    • Monad consists of Kleisli arrows
    • Call a diagonal arrow a Kleisli arrowM(*) M(*) f * *
    • Composition of 2 Kleisli arrowsM(*) M(*) M(*) f g * * *
    • Composition of 2 Kleisli arrows sub f>=>g { f(@_)->flat_map(&g) }M(*) M(*) M(*) f>=>g * * *
    • The 2 laws of Kleisli arrows(i.e. Monad laws)
    • 1) Identity sub unit { ... } M(*) f>=>unit * M(*) M(*) M(*) unit f unit* * * M(*) unit>=>f*
    • 2) Associativity f>=>(g>=>h) M(*)* M(*) M(*)* * M(*) M(*) M(*) f g h* * * M(*) M(*)* (f>=>g)>=>h * M(*)
    • Consider >=> as “a programmable ;“
    • Monads are made of 3 things
    • 1. Nested types MM(M(*)) M(M(*)) M(M(*)) M(M(*)) M(M(*))M(*) M(*) M(*) M(*) M(*) * * * * *
    • 2. A set of arrows to wrap valuesM(M(*)) M(M(*)) M(M(*)) M(M(*)) M(M(*)) unit unit unit unitM(*) M(*) M(*) M(*) M(*) unit unit unit unit * * * * *
    • 3. The operation to lift up arrowsM(M(*)) M(M(*)) M(M(*)) M(M(*)) M(M(*)) flat_map flat_map flat_map flat_mapM(*) M(*) M(*) M(*) M(*) flat_map flat_map flat_map flat_map * * * * *
    • POINT: M extends valuesM(*) $m5 $m3 $m1 $m2 $m4 * 1 “foo” undef $obj
    • POINT: M extends valuesM(*) $m5 $m3 $m1 $m2 $m4 unit(1) unit(“foo”) unit(undef) unit($obj) unit * 1 “foo” undef $obj
    • ex). List[*] [] [$obj1, $obj2, $obj3] [1, 2, 3] [“foo”, “bar”] [undef, undef] [1] [“foo”] [undef] [$obj]* 1 “foo” undef $obj
    • Monads provideextended values and its computations
    • Let’s implement the List Monad
    • All you have to do isdefine a type and unit and flat_map.
    • Implementation of List Monad sub unit { [$_[0]] } sub flat_map { my ($m, $f) = @_; [map { @{$f->($_)} } @$m]; }
    • Thats all we need!
    • Well, are you interested in monads law?The check is left as an exercise :p
    • Simple usage of List monads [3, 5] [$n + 1, ..., $n + 6] roll_dice $nsub roll_dice { [map { $_[0] + $_ } 1 .. 6] }
    • Simple usage of List monads roll_dice’ [4, 5, ..., 9, [3, 5] 6, 7, ..., 11] flat_mapsub roll_dice’ { flat_map $_[0] => &roll_dice }
    • When we define a monad, it comes with 2 relative functions.
    • 1. map map(go_to_jail)[3, 5] [0, 0] map go_to_jail $n 0
    • 1. map map(go_to_jail) [3, 5] [0, 0]sub map_ { my ($m, $f) = @_; map flat_map $m => sub { unit($f->($_[0])) };} go_to_jail $n 0
    • 2. flattenM(M(M(*))) M(M(M(*))) M(M(M(*))) M(M(M(*))) M(M(M(*))) flatten flatten flatten flatten flattM(M(*)) M(M(*)) M(M(*)) M(M(*)) M(M(*)) flatten flatten flatten flatten flatt M(*) M(*) M(*) M(*) M(*) * * * * *
    • Implementation of flatten flattenM(M(*)) M(*) flat_map id M(*) *
    • Implementation of flatten flattensub flatten { M(M(*)) M(*) my $m = shift; flat_map $m => &id; flat_map} id M(*) *
    • flatten() has animportant role on monads.
    • Reduction of terms 6 3 2 8 + 9 2 8 + 11 8 + 19
    • flatten() plays the same role M (M (M (M (*)))) flatten M (M (M (*))) flatten M (M (*)) flatten M (*)
    • Can you illustrateanother example of monads?
    • AnyEvent
    • Sample code of AEmy $cv = AE::cv;http_get "http://yapcasia.org/2011/", sub { my ($data, $hdr) = @_; $cv->send($hdr->{content-length});};print $cv->recv;
    • Sample code of AEmy $cv = AE::cv;http_get "http://yapcasia.org/2011/", sub { my ($data, $hdr) = @_; $cv->send($hdr->{content-length});};print $cv->recv;
    • $cv representsa future value.
    • CondVar isn’t a normal value# !! Can’t write in this way !!sub some_callback { my $cv = shift; print $cv, “n”;}
    • Retrieve the value from $cv sub some_callback { my $cv = shift; print $cv->recv, “n”; }
    • Run it !% perl ./my_great_app.plEV: error in callback (ignoring):AnyEvent::CondVar: recursive blocking waitattempted at ./my_great_app.pl line 9836
    • You must use cb(). sub some_callback { my $cv = shift; $cv->cb(sub { print $_[0]->recv, "n"; }); }
    • Use cb()sub some_callback { my $cv = shift; $cv->cb(sub { my $cv = next_task1 $_[0]->recv; $cv->cb(sub { my $cv = next_task2 $_[0]->recv; ... }); });}
    • Use cb()sub some_callback { my $cv = shift; $cv->cb(sub { my $cv = next_task1 $_[0]->recv; $cv->cb(sub { my $cv = next_task2 $_[0]->recv; $cv->cb(sub { my $cv = next_task3 $_[0]->recv; $cv->cb(sub { my $cv = next_task4 $_[0]->recv; ... }); }); }); });}
    • $cv->cb(sub { my $cv = next_task2 $_[0]->recv; $cv->cb(sub { Use cb() my $cv = next_task3 $_[0]->recv; $cv->cb(sub { my $cv = next_task4 $_[0]->recv; $cv->cb(sub { my $cv = next_task5 $_[0]->recv; $cv->cb(sub { my $cv = next_task6 $_[0]->recv; $cv->cb(sub { my $cv = next_task7 $_[0]->recv; $cv->cb(sub { my $cv = next_task8 $_[0]->recv; $cv->cb(sub { my $cv = next_task9 $_[0]->recv; $cv->cb(sub { my $cv = next_task10 $_[0]->recv; ... }); }); }); }); }); });
    • A callback-hell
    • Coro solves the problem
    • Use Coro::AnyEventsub some_callback { my $cv = shift; async { my $cv1 = next_task1($cv->recv); my $cv2 = next_task2($cv1->recv); my $cv3 = next_task3($cv2->recv); my $cv4 = next_task4($cv3->recv); ... };}
    • Looks perfect!
    • Though, Coro does some deep magic
    • Organize the chaos by the monad pattern
    • Let’s define a type andunit and flat_map
    • AE::CondVar is the type# CondVar(STR)my $cv = AE::cv;$cv->send(‘Normal value’);# CondVar(CondVar(Str))my $cvcv = AE::cv;$cvcv->send($cv);# CondVar(CondVar(CondVar(Str)))my $cvcvcv = AE::cv;$cvcvcv->send($cvcv);
    • Which CondVar objs do normal values correspond to?
    • We can retrieve a normal value without delay sub unit { my @values = @_ my $cv = AE::cv; $cv->send(@values); return $cv; }
    • Think about flat_map() “foo” “Hi, $_[0]”after 3 sec after 2 sec flat_map say_hi $_[0] *
    • Think about flat_map()$name->flat_map(&say_hi) 5 sec “Hi, foo”say_hi 2 sec “Hi, foo”$name 3 sec “foo”
    • Implementation of flat_mapsub flat_map { my ($cv, $f) = @_; $cv->cb(sub { my @values = $_[0]->recv; my $cv = $f->(@values); ... }) return ...}
    • Implementation of flat_mapsub flat_map { my ($cv, $f) = @_; my $result = AE::cv; $cv->cb(sub { my @values = $_[0]->recv; my $cv = $f->(@values); $cv->cb(sub { $result->send($_[0]->recv) }); }); return $result;}
    • A monad comes with map and flatten
    • Use the CondVar monad$cv ∋CV(*) CV(*) CV(*)flat_map flat_map next_task1 next_task2 * * *
    • Use the CondVar monad$cv sub some_callback { ∋ my $cv = shift;CV(*) CV(*) CV(*) $cv->flat_map(&next_task1)flat_map flat_map ->flat_map(&next_task2) ->flat_map(&next_task3) ->flat_map(&next_task4) ->... } next_task1 next_task2 * * *
    • The CV monad has thecontinuation monad structure newtype Cont r a = Cont { runCont :: (a -> r) -> r } runCont cv $ v -> print v $cv->cb(sub { my @v = $_[0]->recv; print @v; });
    • The CV monad also has the Either monad structuredata Either String a = Left String | Right a(my $right = AE::cv)->send(“A right value”);(my $left = AE::cv)->croak(“A left value”);
    • Handle exceptions in flat_mapLeft l >>= _ = Left lRight r >>= f = f r...my $result = AE::cv;$cv->cb(sub { my @r = eval { $_[0]->recv }; return $result->croak($@) if $@; my $cv = $f->(@r); ...});...
    • Define subs to handle errors sub fail { my @values = @_ my $cv = AE::cv; $cv->croak(@values); return $cv; }
    • Define subs to handle errors sub catch { ... my $result = AE::cv; $cv->cb(sub { my @r = eval { $_[0]->recv }; return $result->send(@r) if @r; my $cv = $f->($@); ... }); ...
    • A sample code of catchunit(1, 0)->flat_map(sub { my @v = eval { $_[0] / $_[1] }; $@ ? fail($@) : unit(@v);})->catch(sub { my $exception = shift; $exception =~ /Illegal division/ ? unit(0) # recover from errors : fail($exception); # rethrow})->flat_map(sub { ...});
    • Does everything go well?
    • NO
    • ConcurrencyCV(*), CV(*) CV(*, *)
    • Concurrency sequenceCV(*), CV(*) CV(*, *)
    • There’re no alchemy. OoCV(*, *) ps! CV(*, *) map id *, * *, *
    • Define it directlysub sequence { my ($cv1, $cv2) = @_; $cv1->flat_map(sub { my @v1 = @_; $cv2->flat_map(sub { my @v2 = @_; unit(@v1, @v2); }); });}
    • Use nested blocks to handle more than one monad $cv1->flat_map(sub { my @v1 = @_; $cv2->flat_map(sub { my @v2 = @_; $cv3->flat_map(sub { my @v3 = @_; $cv4->flat_map(sub { my @v4 = @_; $cv5->flat_map(sub { my @v5 = @_; ...
    • Back to the hell
    • Haskell’s do expressiondo v1 <- cv1 v2 <- cv2 v3 <- cv3 v4 <- cv4 ... return (v1, v2, v3, v4, ...)
    • The for comprehensionData::Monad::Base::Sugar::for { pick my @v1 => sub { $cv1 }; pick my @v2 => sub { $cv2 }; pick my @v3 => sub { $cv3 }; pick my @v4 => sub { $cv4 }; ... yield { @v1, @v2, @v3, @v4, ... };};
    • ex). Better implementation of sequence sub sequence { my ($cv1, $cv2) = @_; $cv1->flat_map(sub { my @v1 = @_; $cv2->flat_map(sub { my @v2 = @_; unit(@v1, @v2); }); }); }
    • ex). Better implementation of sequence sub sequence { my ($cv1, $cv2) = @_; Data::Monad::Base::Sugar::for { pick my @v1 => sub { $cv1 }; pick my @v2 => sub { $cv2 }; yield { @v1, @v2 }; }; }
    • Conclusion A monad is made of a type, flat_map, unit Consider AE::cv as a monad CondVar monads save you from a callback hellhttps://github.com/hiratara/p5-Data-Monad