summaryrefslogtreecommitdiff
path: root/lib/Future
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-03-10 19:55:44 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-03-10 19:55:44 +0000
commit8cc5160aefb2ba3611d1d5d6b12b996227f9da72 (patch)
tree256923c9d568f659ca72d254993e6a40c439a7b5 /lib/Future
downloadFuture-tarball-master.tar.gz
Diffstat (limited to 'lib/Future')
-rw-r--r--lib/Future/Phrasebook.pod500
-rw-r--r--lib/Future/Utils.pm687
2 files changed, 1187 insertions, 0 deletions
diff --git a/lib/Future/Phrasebook.pod b/lib/Future/Phrasebook.pod
new file mode 100644
index 0000000..2798536
--- /dev/null
+++ b/lib/Future/Phrasebook.pod
@@ -0,0 +1,500 @@
+# You may distribute under the terms of either the GNU General Public License
+# or the Artistic License (the same terms as Perl itself)
+#
+# (C) Paul Evans, 2013-2014 -- leonerd@leonerd.org.uk
+
+=head1 NAME
+
+C<Future::Phrasebook> - coding examples for C<Future> and C<Future::Utils>
+
+This documentation-only module provides a phrasebook-like approach to giving
+examples on how to use L<Future> and L<Future::Utils> to structure
+Future-driven asynchronous or concurrent logic. As with any inter-dialect
+phrasebook it is structured into pairs of examples; each given first in a
+traditional call/return Perl style, and second in a style using Futures. In
+each case, the generic function or functions in the example are named in
+C<ALL_CAPITALS()> to make them stand out.
+
+In the examples showing use of Futures, any function that is expected to
+return a C<Future> instance is named with a leading C<F_> prefix. Each example
+is also constructed so as to yield an overall future in a variable called
+C<$f>, which represents the entire operation.
+
+=head1 SEQUENCING
+
+The simplest example of a sequencing operation is simply running one piece of
+code, then immediately running a second. In call/return code we can just place
+one after the other.
+
+ FIRST();
+ SECOND();
+
+Using a Future it is necessary to await the result of the first C<Future>
+before calling the second.
+
+ my $f = F_FIRST()
+ ->then( sub { F_SECOND(); } );
+
+Here, the anonymous closure is invoked once the C<Future> returned by
+C<F_FIRST()> succeeds. Because C<then> invokes the code block only if the
+first Future succeeds, it shortcircuits around failures similar to the way that
+C<die()> shortcircuits around thrown exceptions. A C<Future> representing the
+entire combination is returned by the method.
+
+Because the C<then> method itself returns a C<Future> representing the
+overall operation, it can itself be further chained.
+
+ FIRST();
+ SECOND();
+ THIRD();
+
+Z<>
+
+ my $f = F_FIRST()
+ ->then( sub { F_SECOND(); } )
+ ->then( sub { F_THIRD(); } );
+
+See below for examples of ways to handle exceptions.
+
+=head2 Passing Results
+
+Often the result of one function can be passed as an argument to another
+function.
+
+ OUTER( INNER() );
+
+The result of the first C<Future> is passed into the code block given to the
+C<then> method.
+
+ my $f = F_INNER()
+ ->then( sub { F_OUTER( @_ ) } );
+
+=head1 CONDITIONALS
+
+It may be that the result of one function call is used to determine whether or
+not another operation is taken.
+
+ if( COND() == $value ) {
+ ACTION();
+ }
+
+Because the C<then_with_f> code block is given the first future in addition to
+its results it can decide whether to call the second function to return a new
+future, or simply return the one it was given.
+
+ my $f = F_COND()
+ ->then_with_f( sub {
+ my ( $f_cond, $result ) = @_;
+ if( $result == $value ) {
+ return F_ACTION();
+ }
+ else {
+ return $f_cond;
+ }
+ });
+
+=head1 EXCEPTION HANDLING
+
+In regular call/return style code, if any function throws an exception, the
+remainder of the block is not executed, the containing C<try> or C<eval> is
+aborted, and control is passed to the corresponding C<catch> or line after the
+C<eval>.
+
+ try {
+ FIRST();
+ }
+ catch {
+ my $e = $_;
+ ERROR( $e );
+ };
+
+The C<else> method on a C<Future> can be used here. It behaves similar to
+C<then>, but is only invoked if the initial C<Future> fails; not if it
+succeeds.
+
+ my $f = F_FIRST()
+ ->else( sub { F_ERROR( @_ ); } );
+
+Alternatively, the second argument to the C<then> method can be applied, which
+is invoked only on case of failure.
+
+ my $f = F_FIRST()
+ ->then( undef, sub { F_ERROR( @_ ); } );
+
+Often it may be the case that the failure-handling code is in fact immediate,
+and doesn't return a C<Future>. In that case, the C<else> code block can
+return an immediate C<Future> instance.
+
+ my $f = F_FIRST()
+ ->else( sub {
+ ERROR( @_ );
+ return Future->done;
+ });
+
+Sometimes the failure handling code simply needs to be aware of the failure,
+but rethrow it further up.
+
+ try {
+ FIRST();
+ }
+ catch {
+ my $e = $_;
+ ERROR( $e );
+ die $e;
+ };
+
+In this case, while the C<else> block could return a new C<Future> failed with
+the same exception, the C<else_with_f> block is passed the failed C<Future>
+itself in addition to the failure details so it can just return that.
+
+ my $f = F_FIRST()
+ ->else_with_f( sub {
+ my ( $f1, @failure ) = @_;
+ ERROR( @failure );
+ return $f1;
+ });
+
+The C<followed_by> method is similar again, though it invokes the code block
+regardless of the success or failure of the initial C<Future>. It can be used
+to create C<finally> semantics. By returning the C<Future> instance that it
+was passed, the C<followed_by> code ensures it doesn't affect the result of
+the operation.
+
+ try {
+ FIRST();
+ }
+ catch {
+ ERROR( $_ );
+ }
+ finally {
+ CLEANUP();
+ };
+
+Z<>
+
+ my $f = F_FIRST()
+ ->else( sub {
+ ERROR( @_ );
+ return Future->done;
+ })
+ ->followed_by( sub {
+ CLEANUP();
+ return shift;
+ });
+
+=head1 ITERATION
+
+To repeat a single block of code multiple times, a C<while> block is often
+used.
+
+ while( COND() ) {
+ FUNC();
+ }
+
+The C<Future::Utils::repeat> function can be used to repeatedly iterate a
+given C<Future>-returning block of code until its ending condition is
+satisfied.
+
+ use Future::Utils qw( repeat );
+ my $f = repeat {
+ F_FUNC();
+ } while => sub { COND() };
+
+Unlike the statement nature of perl's C<while> block, this C<repeat> C<Future>
+can yield a value; the value returned by C<< $f->get >> is the result of the
+final trial of the code block.
+
+Here, the condition function it expected to return its result immediately. If
+the repeat condition function itself returns a C<Future>, it can be combined
+along with the loop body. The trial C<Future> returned by the code block is
+passed to the C<while> condition function.
+
+ my $f = repeat {
+ F_FUNC()
+ ->followed_by( sub { F_COND(); } );
+ } while => sub { shift->get };
+
+The condition can be negated by using C<until> instead
+
+ until( HALTING_COND() ) {
+ FUNC();
+ }
+
+Z<>
+
+ my $f = repeat {
+ F_FUNC();
+ } until => sub { HALTING_COND() };
+
+=head2 Iterating with Exceptions
+
+Technically, this loop isn't quite the same as the equivalent C<while> loop in
+plain Perl, because the C<while> loop will also stop executing if the code
+within it throws an exception. This can be handled in C<repeat> by testing for
+a failed C<Future> in the C<until> condition.
+
+ while(1) {
+ TRIAL();
+ }
+
+Z<>
+
+ my $f = repeat {
+ F_TRIAL();
+ } until => sub { shift->failure };
+
+When a repeat loop is required to retry a failure, the C<try_repeat> function
+should be used. Currently this function behaves equivalently to C<repeat>,
+except that it will not print a warning if it is asked to retry after a
+failure, whereas this behaviour is now deprecated for the regular C<repeat>
+function so that yields a warning.
+
+ my $f = try_repeat {
+ F_TRIAL();
+ } while => sub { shift->failure };
+
+Another variation is the C<try_repeat_until_success> function, which provides
+a convenient shortcut to calling C<try_repeat> with a condition that makes
+another attempt each time the previous one fails; stopping once it achieves a
+successful result.
+
+ while(1) {
+ eval { TRIAL(); 1 } and last;
+ }
+
+Z<>
+
+ my $f = try_repeat_until_success {
+ F_TRIAL();
+ };
+
+=head2 Iterating over a List
+
+A variation on the idea of the C<while> loop is the C<foreach> loop; a loop
+that executes once for each item in a given list, with a variable set to one
+value from that list each time.
+
+ foreach my $thing ( @THINGS ) {
+ INSPECT( $thing );
+ }
+
+This can be performed with C<Future> using the C<foreach> parameter to the
+C<repeat> function. When this is in effect, the block of code is passed each
+item of the given list as the first parameter.
+
+ my $f = repeat {
+ my $thing = shift;
+ F_INSPECT( $thing );
+ } foreach => \@THINGS;
+
+=head2 Recursing over a Tree
+
+A regular call/return function can use recursion to walk over a tree-shaped
+structure, where each item yields a list of child items.
+
+ sub WALK
+ {
+ my ( $item ) = @_;
+ ...
+ WALK($_) foreach CHILDREN($item);
+ }
+
+This recursive structure can be turned into a C<while()>-based repeat loop by
+using an array to store the remaining items to walk into, instead of using the
+perl stack directly:
+
+ sub WALK
+ {
+ my @more = ( $root );
+ while( @more ) {
+ my $item = shift @more;
+ ...
+ unshift @more, CHILDREN($item)
+ }
+ }
+
+This arrangement then allows us to use C<fmap_void> to walk this structure
+using Futures, possibly concurrently. A lexical array variable is captured
+that holds the stack of remaining items, which is captured by the item code so
+it can C<unshift> more into it, while also being used as the actual C<fmap>
+control array.
+
+ my @more = ( $root );
+
+ my $f = fmap_void {
+ my $item = shift;
+ ...->on_done( sub {
+ unshift @more, @CHILDREN;
+ })
+ } foreach => \@more;
+
+By choosing to either C<unshift> or C<push> more items onto this list, the
+tree can be walked in either depth-first or breadth-first order.
+
+=head1 SHORT-CIRCUITING
+
+Sometimes a result is determined that should be returned through several
+levels of control structure. Regular Perl code has such keywords as C<return>
+to return a value from a function immediately, or C<last> for immediately
+stopping execution of a loop.
+
+ sub func {
+ foreach my $item ( @LIST ) {
+ if( COND($item) ) {
+ return $item;
+ }
+ }
+ return MAKE_NEW_ITEM();
+ }
+
+The C<Future::Utils::call_with_escape> function allows this general form of
+control flow, by calling a block of code that is expected to return a future,
+and itself returning a future. Under normal circumstances the result of this
+future propagates through to the one returned by C<call_with_escape>.
+
+However, the code is also passed in a future value, called here the "escape
+future". If the code captures this future and completes it (either by calling
+C<done> or C<fail>), then the overall returned future immediately completes
+with that result instead, and the future returned by the code block is
+cancelled.
+
+ my $f = call_with_escape {
+ my $escape_f = shift;
+
+ ( repeat {
+ my $item = shift;
+ COND($item)->then( sub {
+ my ( $result ) = @_;
+ if( $result ) {
+ $escape_f->done( $item );
+ }
+ return Future->done;
+ })
+ } foreach => \@ITEMS )->then( sub {
+ MAKE_NEW_ITEM();
+ });
+ };
+
+Here, if C<$escape_f> is completed by the condition test, the future chain
+returned by the code (that is, the C<then> chain of the C<repeat> block
+followed by C<MAKE_NEW_ITEM()>) will be cancelled, and C<$f> itself will
+receive this result.
+
+=head1 CONCURRENCY
+
+This final section of the phrasebook demonstrates a number of abilities that
+are simple to do with C<Future> but can't easily be done with regular
+call/return style programming, because they all involve an element of
+concurrency. In these examples the comparison with regular call/return code
+will be somewhat less accurate because of the inherent ability for the
+C<Future>-using version to behave concurrently.
+
+=head2 Waiting on Multiple Functions
+
+The C<< Future->wait_all >> constructor creates a C<Future> that waits for all
+of the component futures to complete. This can be used to form a sequence with
+concurrency.
+
+ { FIRST_A(); FIRST_B() }
+ SECOND();
+
+Z<>
+
+ my $f = Future->wait_all( FIRST_A(), FIRST_B() )
+ ->then( sub { SECOND() } );
+
+Unlike in the call/return case, this can perform the work of C<FIRST_A()> and
+C<FIRST_B()> concurrently, only proceeding to C<SECOND()> when both are ready.
+
+The result of the C<wait_all> C<Future> is the list of its component
+C<Future>s. This can be used to obtain the results.
+
+ SECOND( FIRST_A(), FIRST_B() );
+
+Z<>
+
+ my $f = Future->wait_all( FIRST_A(), FIRST_B() )
+ ->then( sub {
+ my ( $f_a, $f_b ) = @_
+ SECOND( $f_a->get, $f_b->get );
+ } );
+
+Because the C<get> method will re-raise an exception caused by a failure of
+either of the C<FIRST> functions, the second stage will fail if any of the
+initial Futures failed.
+
+As this is likely to be the desired behaviour most of the time, this kind of
+control flow can be written slightly neater using C<< Future->needs_all >>
+instead.
+
+ my $f = Future->needs_all( FIRST_A(), FIRST_B() )
+ ->then( sub { SECOND( @_ ) } );
+
+The C<get> method of a C<needs_all> convergent Future returns a concatenated
+list of the results of all its component Futures, as the only way it will
+succeed is if all the components do.
+
+=head2 Waiting on Multiple Calls of One Function
+
+Because the C<wait_all> and C<needs_all> constructors take an entire list of
+C<Future> instances, they can be conveniently used with C<map> to wait on the
+result of calling a function concurrently once per item in a list.
+
+ my @RESULT = map { FUNC( $_ ) } @ITEMS;
+ PROCESS( @RESULT );
+
+Again, the C<needs_all> version allows more convenient access to the list of
+results.
+
+ my $f = Future->needs_all( map { F_FUNC( $_ ) } @ITEMS )
+ ->then( sub {
+ my @RESULT = @_;
+ F_PROCESS( @RESULT )
+ } );
+
+This form of the code starts every item's future concurrently, then waits for
+all of them. If the list of C<@ITEMS> is potentially large, this may cause a
+problem due to too many items running at once. Instead, the
+C<Future::Utils::fmap> family of functions can be used to bound the
+concurrency, keeping at most some given number of items running, starting new
+ones as existing ones complete.
+
+ my $f = fmap {
+ my $item = shift;
+ F_FUNC( $item )
+ } foreach => \@ITEMS;
+
+By itself, this will not actually act concurrently as it will only keep one
+Future outstanding at a time. The C<concurrent> flag lets it keep a larger
+number "in flight" at any one time:
+
+ my $f = fmap {
+ my $item = shift;
+ F_FUNC( $item )
+ } foreach => \@ITEMS, concurrent => 10;
+
+The C<fmap> and C<fmap_scalar> functions return a Future that will eventually
+give the collected results of the individual item futures, thus making them
+similar to perl's C<map> operator.
+
+Sometimes, no result is required, and the items are run in a loop simply for
+some side-effect of the body.
+
+ foreach my $item ( @ITEMS ) {
+ FUNC( $item );
+ }
+
+To avoid having to collect a potentially-large set of results only to throw
+them away, the C<fmap_void> function variant of the C<fmap> family yields a
+Future that completes with no result after all the items are complete.
+
+ my $f = fmap_void {
+ my $item = shift;
+ F_FIRST( $item )
+ } foreach => \@ITEMS, concurrent => 10;
+
+=head1 AUTHOR
+
+Paul Evans <leonerd@leonerd.org.uk>
+
+=cut
diff --git a/lib/Future/Utils.pm b/lib/Future/Utils.pm
new file mode 100644
index 0000000..563f327
--- /dev/null
+++ b/lib/Future/Utils.pm
@@ -0,0 +1,687 @@
+# You may distribute under the terms of either the GNU General Public License
+# or the Artistic License (the same terms as Perl itself)
+#
+# (C) Paul Evans, 2013-2015 -- leonerd@leonerd.org.uk
+
+package Future::Utils;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.32';
+
+use Exporter 'import';
+# Can't import the one from Exporter as it relies on package inheritance
+sub export_to_level
+{
+ my $pkg = shift; local $Exporter::ExportLevel = 1 + shift; $pkg->import(@_);
+}
+
+our @EXPORT_OK = qw(
+ call
+ call_with_escape
+
+ repeat
+ try_repeat try_repeat_until_success
+ repeat_until_success
+
+ fmap fmap_concat
+ fmap1 fmap_scalar
+ fmap0 fmap_void
+);
+
+use Carp;
+our @CARP_NOT = qw( Future );
+
+use Future;
+
+=head1 NAME
+
+C<Future::Utils> - utility functions for working with C<Future> objects
+
+=head1 SYNOPSIS
+
+ use Future::Utils qw( call_with_escape );
+
+ my $result_f = call_with_escape {
+ my $escape_f = shift;
+ my $f = ...
+ $escape_f->done( "immediate result" );
+ ...
+ };
+
+Z<>
+
+ use Future::Utils qw( repeat try_repeat try_repeat_until_success );
+
+ my $eventual_f = repeat {
+ my $trial_f = ...
+ return $trial_f;
+ } while => sub { my $f = shift; return want_more($f) };
+
+ my $eventual_f = repeat {
+ ...
+ return $trail_f;
+ } until => sub { my $f = shift; return acceptable($f) };
+
+ my $eventual_f = repeat {
+ my $item = shift;
+ ...
+ return $trial_f;
+ } foreach => \@items;
+
+ my $eventual_f = try_repeat {
+ my $trial_f = ...
+ return $trial_f;
+ } while => sub { ... };
+
+ my $eventual_f = try_repeat_until_success {
+ ...
+ return $trial_f;
+ };
+
+ my $eventual_f = try_repeat_until_success {
+ my $item = shift;
+ ...
+ return $trial_f;
+ } foreach => \@items;
+
+Z<>
+
+ use Future::Utils qw( fmap_concat fmap_scalar fmap_void );
+
+ my $result_f = fmap_concat {
+ my $item = shift;
+ ...
+ return $item_f;
+ } foreach => \@items, concurrent => 4;
+
+ my $result_f = fmap_scalar {
+ my $item = shift;
+ ...
+ return $item_f;
+ } foreach => \@items, concurrent => 8;
+
+ my $done_f = fmap_void {
+ my $item = shift;
+ ...
+ return $item_f;
+ } foreach => \@items, concurrent => 10;
+
+=cut
+
+=head1 INVOKING A BLOCK OF CODE
+
+=head2 $f = call { CODE }
+
+The C<call> function invokes a block of code that returns a future, and simply
+returns the future it returned. The code is wrapped in an C<eval {}> block, so
+that if it throws an exception this is turned into an immediate failed
+C<Future>. If the code does not return a C<Future>, then an immediate failed
+C<Future> instead.
+
+(This is equivalent to using C<< Future->call >>, but is duplicated here for
+completeness).
+
+=cut
+
+sub call(&)
+{
+ my ( $code ) = @_;
+ return Future->call( $code );
+}
+
+=head2 $f = call_with_escape { CODE }
+
+The C<call_with_escape> function invokes a block of code that returns a
+future, and passes in a separate future (called here an "escape future").
+Normally this is equivalent to the simple C<call> function. However, if the
+code captures this future and completes it by calling C<done> or C<fail> on
+it, the future returned by C<call_with_escape> immediately completes with this
+result, and the future returned by the code itself is cancelled.
+
+This can be used to implement short-circuit return from an iterating loop or
+complex sequence of code, or immediate fail that bypasses failure handling
+logic in the code itself, or several other code patterns.
+
+ $f = $code->( $escape_f )
+
+(This can be considered similar to C<call-with-escape-continuation> as found
+in some Scheme implementations).
+
+=cut
+
+sub call_with_escape(&)
+{
+ my ( $code ) = @_;
+
+ my $escape_f = Future->new;
+
+ return Future->wait_any(
+ Future->call( $code, $escape_f ),
+ $escape_f,
+ );
+}
+
+=head1 REPEATING A BLOCK OF CODE
+
+The C<repeat> function provides a way to repeatedly call a block of code that
+returns a L<Future> (called here a "trial future") until some ending condition
+is satisfied. The C<repeat> function itself returns a C<Future> to represent
+running the repeating loop until that end condition (called here the "eventual
+future"). The first time the code block is called, it is passed no arguments,
+and each subsequent invocation is passed the previous trial future.
+
+The result of the eventual future is the result of the last trial future.
+
+If the eventual future is cancelled, the latest trial future will be
+cancelled.
+
+If some specific subclass or instance of C<Future> is required as the return
+value, it can be passed as the C<return> argument. Otherwise the return value
+will be constructed by cloning the first non-immediate trial C<Future>.
+
+=head2 $future = repeat { CODE } while => CODE
+
+Repeatedly calls the C<CODE> block while the C<while> condition returns a true
+value. Each time the trial future completes, the C<while> condition is passed
+the trial future.
+
+ $trial_f = $code->( $previous_trial_f )
+ $again = $while->( $trial_f )
+
+If the C<$code> block dies entirely and throws an exception, this will be
+caught and considered as an immediately-failed C<Future> with the exception as
+the future's failure. The exception will not be propagated to the caller.
+
+=head2 $future = repeat { CODE } until => CODE
+
+Repeatedly calls the C<CODE> block until the C<until> condition returns a true
+value. Each time the trial future completes, the C<until> condition is passed
+the trial future.
+
+ $trial_f = $code->( $previous_trial_f )
+ $accept = $until->( $trial_f )
+
+=head2 $future = repeat { CODE } foreach => ARRAY, otherwise => CODE
+
+Calls the C<CODE> block once for each value obtained from the array, passing
+in the value as the first argument (before the previous trial future). When
+there are no more items left in the array, the C<otherwise> code is invoked
+once and passed the last trial future, if there was one, or C<undef> if the
+list was originally empty. The result of the eventual future will be the
+result of the future returned from C<otherwise>.
+
+The referenced array may be modified by this operation.
+
+ $trial_f = $code->( $item, $previous_trial_f )
+ $final_f = $otherwise->( $last_trial_f )
+
+The C<otherwise> code is optional; if not supplied then the result of the
+eventual future will simply be that of the last trial. If there was no trial,
+because the C<foreach> list was already empty, then an immediate successful
+future with an empty result is returned.
+
+=head2 $future = repeat { CODE } foreach => ARRAY, while => CODE, ...
+
+=head2 $future = repeat { CODE } foreach => ARRAY, until => CODE, ...
+
+Combines the effects of C<foreach> with C<while> or C<until>. Calls the
+C<CODE> block once for each value obtained from the array, until the array is
+exhausted or the given ending condition is satisfied.
+
+If a C<while> or C<until> condition is combined with C<otherwise>, the
+C<otherwise> code will only be run if the array was entirely exhausted. If the
+operation is terminated early due to the C<while> or C<until> condition being
+satisfied, the eventual result will simply be that of the last trial that was
+executed.
+
+=head2 $future = repeat { CODE } generate => CODE, otherwise => CODE
+
+Calls the C<CODE> block once for each value obtained from the generator code,
+passing in the value as the first argument (before the previous trial future).
+When the generator returns an empty list, the C<otherwise> code is invoked and
+passed the last trial future, if there was one, otherwise C<undef> if the
+generator never returned a value. The result of the eventual future will be
+the result of the future returned from C<otherwise>.
+
+ $trial_f = $code->( $item, $previous_trial_f )
+ $final_f = $otherwise->( $last_trial_f )
+
+ ( $item ) = $generate->()
+
+The generator is called in list context but should return only one item per
+call. Subsequent values will be ignored. When it has no more items to return
+it should return an empty list.
+
+For backward compatibility this function will allow a C<while> or C<until>
+condition that requests a failure be repeated, but it will print a warning if
+it has to do that. To apply repeating behaviour that can catch and retry
+failures, use C<try_repeat> instead. This old behaviour is now deprecated and
+will be removed in the next version.
+
+=cut
+
+sub _repeat
+{
+ my ( $code, $return, $trialp, $cond, $sense, $is_try ) = @_;
+
+ my $prev = $$trialp;
+
+ while(1) {
+ my $trial = $$trialp ||= Future->call( $code, $prev );
+ $prev = $trial;
+
+ if( !$trial->is_ready ) {
+ # defer
+ $return ||= $trial->new;
+ $trial->on_ready( sub {
+ return if $$trialp->is_cancelled;
+ _repeat( $code, $return, $trialp, $cond, $sense, $is_try );
+ });
+ return $return;
+ }
+
+ my $stop;
+ if( not eval { $stop = !$cond->( $trial ) ^ $sense; 1 } ) {
+ $return ||= $trial->new;
+ $return->fail( $@ );
+ return $return;
+ }
+
+ if( $stop ) {
+ # Return result
+ $return ||= $trial->new;
+ $trial->on_done( $return );
+ $trial->on_fail( $return );
+ return $return;
+ }
+
+ if( !$is_try and $trial->failure ) {
+ carp "Using Future::Utils::repeat to retry a failure is deprecated; use try_repeat instead";
+ }
+
+ # redo
+ undef $$trialp;
+ }
+}
+
+sub repeat(&@)
+{
+ my $code = shift;
+ my %args = @_;
+
+ # This makes it easier to account for other conditions
+ defined($args{while}) + defined($args{until}) == 1
+ or defined($args{foreach})
+ or defined($args{generate})
+ or croak "Expected one of 'while', 'until', 'foreach' or 'generate'";
+
+ if( $args{foreach} ) {
+ $args{generate} and croak "Cannot use both 'foreach' and 'generate'";
+
+ my $array = delete $args{foreach};
+ $args{generate} = sub {
+ @$array ? shift @$array : ();
+ };
+ }
+
+ if( $args{generate} ) {
+ my $generator = delete $args{generate};
+ my $otherwise = delete $args{otherwise};
+
+ # TODO: This is slightly messy as this lexical is captured by both
+ # blocks of code. Can we do better somehow?
+ my $done;
+
+ my $orig_code = $code;
+ $code = sub {
+ my ( $last_trial_f ) = @_;
+ my $again = my ( $value ) = $generator->( $last_trial_f );
+
+ if( $again ) {
+ unshift @_, $value; goto &$orig_code;
+ }
+
+ $done++;
+ if( $otherwise ) {
+ goto &$otherwise;
+ }
+ else {
+ return $last_trial_f || Future->done;
+ }
+ };
+
+ if( my $orig_while = delete $args{while} ) {
+ $args{while} = sub {
+ $orig_while->( $_[0] ) and !$done;
+ };
+ }
+ elsif( my $orig_until = delete $args{until} ) {
+ $args{while} = sub {
+ !$orig_until->( $_[0] ) and !$done;
+ };
+ }
+ else {
+ $args{while} = sub { !$done };
+ }
+ }
+
+ my $future = $args{return};
+
+ my $trial;
+ $args{while} and $future = _repeat( $code, $future, \$trial, $args{while}, 0, $args{try} );
+ $args{until} and $future = _repeat( $code, $future, \$trial, $args{until}, 1, $args{try} );
+
+ $future->on_cancel( sub { $trial->cancel } );
+
+ return $future;
+}
+
+=head2 $future = try_repeat { CODE } ...
+
+A variant of C<repeat> that doesn't warn when the trial fails and the
+condition code asks for it to be repeated.
+
+In some later version the C<repeat> function will be changed so that if a
+trial future fails, then the eventual future will immediately fail as well,
+making its semantics a little closer to that of a C<while {}> loop in Perl.
+Code that specifically wishes to catch failures in trial futures and retry
+the block should use C<try_repeat> specifically.
+
+=cut
+
+sub try_repeat(&@)
+{
+ # defeat prototype
+ &repeat( @_, try => 1 );
+}
+
+=head2 $future = try_repeat_until_success { CODE } ...
+
+A shortcut to calling C<try_repeat> with an ending condition that simply tests
+for a successful result from a future. May be combined with C<foreach> or
+C<generate>.
+
+This function used to be called C<repeat_until_success>, and is currently
+aliased as this name as well.
+
+=cut
+
+sub try_repeat_until_success(&@)
+{
+ my $code = shift;
+ my %args = @_;
+
+ # TODO: maybe merge while/until conditions one day...
+ defined($args{while}) or defined($args{until})
+ and croak "Cannot pass 'while' or 'until' to try_repeat_until_success";
+
+ # defeat prototype
+ &try_repeat( $code, while => sub { shift->failure }, %args );
+}
+
+# Legacy name
+*repeat_until_success = \&try_repeat_until_success;
+
+=head1 APPLYING A FUNCTION TO A LIST
+
+The C<fmap> family of functions provide a way to call a block of code that
+returns a L<Future> (called here an "item future") once per item in a given
+list, or returned by a generator function. The C<fmap*> functions themselves
+return a C<Future> to represent the ongoing operation, which completes when
+every item's future has completed.
+
+While this behaviour can also be implemented using C<repeat>, the main reason
+to use an C<fmap> function is that the individual item operations are
+considered as independent, and thus more than one can be outstanding
+concurrently. An argument can be passed to the function to indicate how many
+items to start initially, and thereafter it will keep that many of them
+running concurrently until all of the items are done, or until any of them
+fail. If an individual item future fails, the overall result future will be
+marked as failing with the same failure, and any other pending item futures
+that are outstanding at the time will be cancelled.
+
+The following named arguments are common to each C<fmap*> function:
+
+=over 8
+
+=item foreach => ARRAY
+
+Provides the list of items to iterate over, as an C<ARRAY> reference.
+
+The referenced array will be modified by this operation, C<shift>ing one item
+from it each time. The can C<push> more items to this array as it runs, and
+they will be included in the iteration.
+
+=item generate => CODE
+
+Provides the list of items to iterate over, by calling the generator function
+once for each required item. The function should return a single item, or an
+empty list to indicate it has no more items.
+
+ ( $item ) = $generate->()
+
+This function will be invoked each time any previous item future has completed
+and may be called again even after it has returned empty.
+
+=item concurrent => INT
+
+Gives the number of item futures to keep outstanding. By default this value
+will be 1 (i.e. no concurrency); larger values indicate that multiple item
+futures will be started at once.
+
+=item return => Future
+
+Normally, a new instance is returned by cloning the first non-immediate future
+returned as an item future. By passing a new instance as the C<return>
+argument, the result will be put into the given instance. This can be used to
+return subclasses, or specific instances.
+
+=back
+
+In each case, the main code block will be called once for each item in the
+list, passing in the item as the only argument:
+
+ $item_f = $code->( $item )
+
+The expected return value from each item's future, and the value returned from
+the result future will differ in each function's case; they are documented
+below.
+
+=cut
+
+# This function is invoked in two circumstances:
+# a) to create an item Future in a slot,
+# b) once a non-immediate item Future is complete, to check its results
+# It can tell which circumstance by whether the slot itself is defined or not
+sub _fmap_slot
+{
+ my ( $slots, undef, $code, $generator, $collect, $results, $return ) = @_;
+
+ SLOT: while(1) {
+ # Capture args each call because we mutate them
+ my ( undef, $idx ) = my @args = @_;
+
+ unless( $slots->[$idx] ) {
+ # No item Future yet (case a), so create one
+ my $item;
+ unless( ( $item ) = $generator->() ) {
+ # All out of items, so now just wait for the slots to be finished
+ undef $slots->[$idx];
+ defined and return $return for @$slots;
+
+ # All the slots are done
+ $return ||= Future->new;
+
+ $return->done( @$results );
+ return $return;
+ }
+
+ my $f = $slots->[$idx] = Future->call( $code, $item );
+
+ if( $collect eq "array" ) {
+ push @$results, my $r = [];
+ $f->on_done( sub { @$r = @_ });
+ }
+ elsif( $collect eq "scalar" ) {
+ push @$results, undef;
+ my $r = \$results->[-1];
+ $f->on_done( sub { $$r = $_[0] });
+ }
+ }
+
+ my $f = $slots->[$idx];
+
+ # Slot is non-immediate; arrange for us to be invoked again later when it's ready
+ if( !$f->is_ready ) {
+ $args[-1] = ( $return ||= $f->new );
+ $f->on_done( sub { _fmap_slot( @args ) } );
+ $f->on_fail( $return );
+
+ # Try looking for more that might be ready
+ my $i = $idx + 1;
+ while( $i != $idx ) {
+ $i++;
+ $i %= @$slots;
+ next if defined $slots->[$i];
+
+ $_[1] = $i;
+ redo SLOT;
+ }
+ return $return;
+ }
+
+ # Either we've been invoked again (case b), or the immediate Future was
+ # already ready.
+ if( $f->failure ) {
+ $return ||= $f->new;
+ $return->fail( $f->failure );
+ return $return;
+ }
+
+ undef $slots->[$idx];
+ # next
+ }
+}
+
+sub _fmap
+{
+ my $code = shift;
+ my %args = @_;
+
+ my $concurrent = $args{concurrent} || 1;
+ my @slots;
+
+ my $results = [];
+ my $future = $args{return};
+
+ my $generator;
+ if( $generator = $args{generate} ) {
+ # OK
+ }
+ elsif( my $array = $args{foreach} ) {
+ $generator = sub { return unless @$array; shift @$array };
+ }
+ else {
+ croak "Expected either 'generate' or 'foreach'";
+ }
+
+ # If any of these immediately fail, don't bother continuing
+ foreach my $idx ( 0 .. $concurrent-1 ) {
+ $future = _fmap_slot( \@slots, $idx, $code, $generator, $args{collect}, $results, $future );
+ last if $future->is_ready;
+ }
+
+ $future->on_fail( sub {
+ !defined $_ or $_->is_ready or $_->cancel for @slots;
+ });
+ $future->on_cancel( sub {
+ $_->cancel for @slots;
+ });
+
+ return $future;
+}
+
+=head2 $future = fmap_concat { CODE } ...
+
+This version of C<fmap> expects each item future to return a list of zero or
+more values, and the overall result will be the concatenation of all these
+results. It acts like a future-based equivalent to Perl's C<map> operator.
+
+The results are returned in the order of the original input values, not in the
+order their futures complete in. Because of the intermediate storage of
+C<ARRAY> references and final flattening operation used to implement this
+behaviour, this function is slightly less efficient than C<fmap_scalar> or
+C<fmap_void> in cases where item futures are expected only ever to return one,
+or zero values, respectively.
+
+This function is also available under the name of simply C<fmap> to emphasise
+its similarity to perl's C<map> keyword.
+
+=cut
+
+sub fmap_concat(&@)
+{
+ my $code = shift;
+ my %args = @_;
+
+ _fmap( $code, %args, collect => "array" )->then( sub {
+ return Future->done( map { @$_ } @_ );
+ });
+}
+*fmap = \&fmap_concat;
+
+=head2 $future = fmap_scalar { CODE } ...
+
+This version of C<fmap> acts more like the C<map> functions found in Scheme or
+Haskell; it expects that each item future returns only one value, and the
+overall result will be a list containing these, in order of the original input
+items. If an item future returns more than one value the others will be
+discarded. If it returns no value, then C<undef> will be substituted in its
+place so that the result list remains in correspondence with the input list.
+
+This function is also available under the shorter name of C<fmap1>.
+
+=cut
+
+sub fmap_scalar(&@)
+{
+ my $code = shift;
+ my %args = @_;
+
+ _fmap( $code, %args, collect => "scalar" )
+}
+*fmap1 = \&fmap_scalar;
+
+=head2 $future = fmap_void { CODE } ...
+
+This version of C<fmap> does not collect any results from its item futures, it
+simply waits for them all to complete. Its result future will provide no
+values.
+
+While not a map in the strictest sense, this variant is still useful as a way
+to control concurrency of a function call iterating over a list of items,
+obtaining its results by some other means (such as side-effects on captured
+variables, or some external system).
+
+This function is also available under the shorter name of C<fmap0>.
+
+=cut
+
+sub fmap_void(&@)
+{
+ my $code = shift;
+ my %args = @_;
+
+ _fmap( $code, %args, collect => "void" )
+}
+*fmap0 = \&fmap_void;
+
+=head1 AUTHOR
+
+Paul Evans <leonerd@leonerd.org.uk>
+
+=cut
+
+0x55AA;