23. 2. A set of arrows
to wrap values
M(M(*)) M(M(*)) M(M(*)) M(M(*)) M(M(*))
unit unit unit unit
M(*) M(*) M(*) M(*) M(*)
unit unit unit unit
* * * * *
50. CondVar isn’t
a normal value
# !! Can’t write in this way !!
sub some_callback {
my $cv = shift;
print $cv, “n”;
}
51. Retrieve the value from $cv
sub some_callback {
my $cv = shift;
print $cv->recv, “n”;
}
52. Run it !
% perl ./my_great_app.pl
EV: error in callback (ignoring):
AnyEvent::CondVar: recursive blocking wait
attempted at ./my_great_app.pl line 9836
53. You must use cb().
sub some_callback {
my $cv = shift;
$cv->cb(sub {
print $_[0]->recv, "n";
});
}
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. 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. $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;
...
});
});
});
});
});
});
59. Use Coro::AnyEvent
sub 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);
...
};
}
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);
72. Use the CondVar monad
$cv
∋
CV(*) CV(*) CV(*)
flat_map flat_map
next_task1 next_task2
* * *
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. The CV monad has the
continuation 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. The CV monad also has the
Either monad structure
data Either String a = Left String | Right a
(my $right = AE::cv)->send(“A right value”);
(my $left = AE::cv)->croak(“A left value”);
76. Handle exceptions in
flat_map
Left l >>= _ = Left l
Right 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. Define subs to handle errors
sub fail {
my @values = @_
my $cv = AE::cv;
$cv->croak(@values);
return $cv;
}
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. A sample code of catch
unit(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 {
...
});
85. Define it directly
sub sequence {
my ($cv1, $cv2) = @_;
$cv1->flat_map(sub {
my @v1 = @_;
$cv2->flat_map(sub {
my @v2 = @_;
unit(@v1, @v2);
});
});
}
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 = @_;
...
89. The for comprehension
Data::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. 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. 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. Conclusion
A monad is made of a type,
flat_map, unit
Consider AE::cv as a monad
CondVar monads save you from a
callback hell
https://github.com/hiratara/p5-Data-Monad