Monads in perl

7,501 views
7,347 views

Published on

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

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
7,501
On SlideShare
0
From Embeds
0
Number of Embeds
329
Actions
Shares
0
Downloads
22
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Monads in perl

  1. 1. Monads in Perlhiratara <hira.tara@gmail.com>
  2. 2. About med.hatena.ne.jp/hirataratwitter.com/hirataraworking as a Perl programmera fan of Mathematicsa reporter of this YAPC
  3. 3. What are Monads?
  4. 4. Actually, monads arenot anything special.
  5. 5. Category theory helps your understanding.
  6. 6. An arrow is a function split Regex, Str, Int [Str] @str = split /$regex/, $str, $n lengthStr Int$len = length $str
  7. 7. Or represents a method newClassName CGI $cgi = CGI->new param CGI, Str [Str] @values = $cgi->param($str)
  8. 8. Connecting two arrows * f g* *
  9. 9. Connecting two arrows * f g f;g * * sub f;g {sub f;g { g(f(@_)) } or my @v = f(@_); g(@v) }
  10. 10. The 2 laws of arrows
  11. 11. 1) Identity sub id { @_ } f;idid f id id;f
  12. 12. 2) Associativity f;(g;h)f g h (f;g);h
  13. 13. Monad consists of Kleisli arrows
  14. 14. Call a diagonal arrow a Kleisli arrowM(*) M(*) f * *
  15. 15. Composition of 2 Kleisli arrowsM(*) M(*) M(*) f g * * *
  16. 16. Composition of 2 Kleisli arrows sub f>=>g { f(@_)->flat_map(&g) }M(*) M(*) M(*) f>=>g * * *
  17. 17. The 2 laws of Kleisli arrows(i.e. Monad laws)
  18. 18. 1) Identity sub unit { ... } M(*) f>=>unit * M(*) M(*) M(*) unit f unit* * * M(*) unit>=>f*
  19. 19. 2) Associativity f>=>(g>=>h) M(*)* M(*) M(*)* * M(*) M(*) M(*) f g h* * * M(*) M(*)* (f>=>g)>=>h * M(*)
  20. 20. Consider >=> as “a programmable ;“
  21. 21. Monads are made of 3 things
  22. 22. 1. Nested types MM(M(*)) M(M(*)) M(M(*)) M(M(*)) M(M(*))M(*) M(*) M(*) M(*) M(*) * * * * *
  23. 23. 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 * * * * *
  24. 24. 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 * * * * *
  25. 25. POINT: M extends valuesM(*) $m5 $m3 $m1 $m2 $m4 * 1 “foo” undef $obj
  26. 26. POINT: M extends valuesM(*) $m5 $m3 $m1 $m2 $m4 unit(1) unit(“foo”) unit(undef) unit($obj) unit * 1 “foo” undef $obj
  27. 27. ex). List[*] [] [$obj1, $obj2, $obj3] [1, 2, 3] [“foo”, “bar”] [undef, undef] [1] [“foo”] [undef] [$obj]* 1 “foo” undef $obj
  28. 28. Monads provideextended values and its computations
  29. 29. Let’s implement the List Monad
  30. 30. All you have to do isdefine a type and unit and flat_map.
  31. 31. Implementation of List Monad sub unit { [$_[0]] } sub flat_map { my ($m, $f) = @_; [map { @{$f->($_)} } @$m]; }
  32. 32. Thats all we need!
  33. 33. Well, are you interested in monads law?The check is left as an exercise :p
  34. 34. Simple usage of List monads [3, 5] [$n + 1, ..., $n + 6] roll_dice $nsub roll_dice { [map { $_[0] + $_ } 1 .. 6] }
  35. 35. Simple usage of List monads roll_dice’ [4, 5, ..., 9, [3, 5] 6, 7, ..., 11] flat_mapsub roll_dice’ { flat_map $_[0] => &roll_dice }
  36. 36. When we define a monad, it comes with 2 relative functions.
  37. 37. 1. map map(go_to_jail)[3, 5] [0, 0] map go_to_jail $n 0
  38. 38. 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
  39. 39. 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(*) * * * * *
  40. 40. Implementation of flatten flattenM(M(*)) M(*) flat_map id M(*) *
  41. 41. Implementation of flatten flattensub flatten { M(M(*)) M(*) my $m = shift; flat_map $m => &id; flat_map} id M(*) *
  42. 42. flatten() has animportant role on monads.
  43. 43. Reduction of terms 6 3 2 8 + 9 2 8 + 11 8 + 19
  44. 44. flatten() plays the same role M (M (M (M (*)))) flatten M (M (M (*))) flatten M (M (*)) flatten M (*)
  45. 45. Can you illustrateanother example of monads?
  46. 46. AnyEvent
  47. 47. 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;
  48. 48. 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;
  49. 49. $cv representsa future value.
  50. 50. CondVar isn’t a normal value# !! Can’t write in this way !!sub some_callback { my $cv = shift; print $cv, “n”;}
  51. 51. Retrieve the value from $cv sub some_callback { my $cv = shift; print $cv->recv, “n”; }
  52. 52. Run it !% perl ./my_great_app.plEV: error in callback (ignoring):AnyEvent::CondVar: recursive blocking waitattempted at ./my_great_app.pl line 9836
  53. 53. You must use cb(). sub some_callback { my $cv = shift; $cv->cb(sub { print $_[0]->recv, "n"; }); }
  54. 54. 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; ... }); });}
  55. 55. 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; ... }); }); }); });}
  56. 56. $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; ... }); }); }); }); }); });
  57. 57. A callback-hell
  58. 58. Coro solves the problem
  59. 59. 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); ... };}
  60. 60. Looks perfect!
  61. 61. Though, Coro does some deep magic
  62. 62. Organize the chaos by the monad pattern
  63. 63. Let’s define a type andunit and flat_map
  64. 64. 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);
  65. 65. Which CondVar objs do normal values correspond to?
  66. 66. We can retrieve a normal value without delay sub unit { my @values = @_ my $cv = AE::cv; $cv->send(@values); return $cv; }
  67. 67. Think about flat_map() “foo” “Hi, $_[0]”after 3 sec after 2 sec flat_map say_hi $_[0] *
  68. 68. Think about flat_map()$name->flat_map(&say_hi) 5 sec “Hi, foo”say_hi 2 sec “Hi, foo”$name 3 sec “foo”
  69. 69. Implementation of flat_mapsub flat_map { my ($cv, $f) = @_; $cv->cb(sub { my @values = $_[0]->recv; my $cv = $f->(@values); ... }) return ...}
  70. 70. 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;}
  71. 71. A monad comes with map and flatten
  72. 72. Use the CondVar monad$cv ∋CV(*) CV(*) CV(*)flat_map flat_map next_task1 next_task2 * * *
  73. 73. 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 * * *
  74. 74. 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; });
  75. 75. 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”);
  76. 76. 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); ...});...
  77. 77. Define subs to handle errors sub fail { my @values = @_ my $cv = AE::cv; $cv->croak(@values); return $cv; }
  78. 78. 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->($@); ... }); ...
  79. 79. 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 { ...});
  80. 80. Does everything go well?
  81. 81. NO
  82. 82. ConcurrencyCV(*), CV(*) CV(*, *)
  83. 83. Concurrency sequenceCV(*), CV(*) CV(*, *)
  84. 84. There’re no alchemy. OoCV(*, *) ps! CV(*, *) map id *, * *, *
  85. 85. Define it directlysub sequence { my ($cv1, $cv2) = @_; $cv1->flat_map(sub { my @v1 = @_; $cv2->flat_map(sub { my @v2 = @_; unit(@v1, @v2); }); });}
  86. 86. 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 = @_; ...
  87. 87. Back to the hell
  88. 88. Haskell’s do expressiondo v1 <- cv1 v2 <- cv2 v3 <- cv3 v4 <- cv4 ... return (v1, v2, v3, v4, ...)
  89. 89. 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, ... };};
  90. 90. 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); }); }); }
  91. 91. 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 }; }; }
  92. 92. 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

×