Future::Utils(3pm)
π NAME π
okay so <pink>Future::Utils</pink> β it's a lil Perl module from the <cyan>User Contributed Perl Documentation</cyan> crew, and it's literally just a grab-bag of utility functions for working with <purple>Future</purple> objects. that's the whole vibe. π¦
β¨ SYNOPSIS β¨
bestie, here's the whole tour, straight up:
use Future::Utils qw( call_with_escape );
my $result_f = call_with_escape {
my $escape_f = shift;
my $f = ...
$escape_f->done( "immediate result" );
...
};
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 $trial_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;
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;
like. look at all that. we're so back. π₯
πΈ DESCRIPTION πΈ
so this module is basically a bag of <hotpink>utility functions</hotpink> for working with Future instances, and the reason they live here instead of being methods on the <purple>Future</purple> class itself is honestly kind of a Perl thing β mostly it's because they're <yellow>prototyped to take a block of code as the first argument</yellow>, and you just can't do that as a method. no cap. π
okay and important: unless it says otherwise somewhere specific, <cyan>every function below needs at least version 0.08</cyan>. noted. π
π INVOKING A BLOCK OF CODE π
π call
$f = call { CODE };
Since version 0.22. β
so call runs a block of code that's supposed to hand you back a future, and then... it just gives you that future. that's it. lowkey iconic in its simplicity.
the code gets wrapped in an <peach>eval {}</peach> block, so if it <red>throws an exception</red>, that exception gets turned into an <red>immediate failed</red> Future``. and if the code doesn't return a Future at all? yeah. also an immediate failed Future. rude but fair. π
(ngl this is literally the same as using Future->call, it's just copied in here for completeness. same thing, different address.)
π«Ά call_with_escape
$f = call_with_escape { CODE };
Since version 0.22. β
call_with_escape also runs a block of code that returns a future β but it also <mint>passes in a separate future</mint> (which we're calling the <hotpink>"escape future"</hotpink> here). normally it behaves exactly like plain old call. nothing weird.
BUT. and this is the good part. if the code grabs that escape future and completes it by calling done or fail on it, then the future returned by call_with_escape <green>immediately completes with that result</green>, and the future the code itself returned gets <red>cancelled</red>. poof. gone. π
what's it for? literally so many things: <yellow>short-circuit return</yellow> from an iterating loop or some messy complex sequence of code, or an <yellow>immediate fail</yellow> that skips right past all the failure handling logic inside the code, or a bunch of other code patterns. it's giving escape hatch energy. πͺβ¨
$f = $code->( $escape_f );
(and yeah, this is basically the call-with-escape-continuation thing you'd find in some Scheme implementations. iykyk.) π§
π¦ REPEATING A BLOCK OF CODE π¦
okay so repeat is the whole concept here β it's how you <pink>repeatedly call a block of code that returns a Future</pink> (which we're calling a <hotpink>"trial future"</hotpink>) until some <cyan>ending condition</cyan> is satisfied. repeat itself hands you back a Future representing the whole run of the loop until that end condition β that one's the <hotpink>"eventual future"</hotpink>. π«Ά
the first time the code block gets called, it gets <yellow>no arguments</yellow>. every call after that gets handed the <yellow>previous trial future</yellow>.
the result of the eventual future is the result of the <pink>last trial future</pink>. periodt.
and if the eventual future gets <red>cancelled</red>? the latest trial future gets cancelled too. they go down together. π
oh and if you need some <cyan>specific subclass or instance of Future</cyan> as the return value, you can pass it as the <purple>"return"</purple> argument. otherwise the return value gets constructed by <yellow>cloning the first non-immediate trial Future</yellow>. cute.
β repeat+while
$future = repeat { CODE } while => CODE;
keeps calling the CODE block <green>while</green> the "while" condition returns a true value. every time a trial future completes, the "while" condition gets handed that trial future. π
$trial_f = $code->( $previous_trial_f );
$again = $while->( $trial_f );
and if the $code block <red>dies entirely</red> and throws an exception? that gets caught, and it's treated as an <yellow>immediately-failed Future</yellow> with the exception as that future's failure. the exception will <red>not</red> be propagated to the caller. no drama leaks out. π
β repeat+until
$future = repeat { CODE } until => CODE;
same deal but inverted: keeps calling the CODE block <green>until</green> the "until" condition returns a true value. every time a trial future completes, the "until" condition gets handed that trial future. π
$trial_f = $code->( $previous_trial_f );
$accept = $until->( $trial_f );
π repeat+foreach
$future = repeat { CODE } foreach => ARRAY, otherwise => CODE;
Since version 0.13. β
this one calls the CODE block <pink>once for each value</pink> pulled out of the array, passing that value in as the <yellow>first argument</yellow> (before the previous trial future). when there's <cyan>no more items left</cyan> in the array, the otherwise code gets invoked <pink>once</pink> and handed the last trial future β or <red>undef</red> if the list was empty to begin with. and the result of the eventual future is the result of the future that otherwise handed back. chef's kiss. π
heads up though: the referenced array <red>may be modified</red> by this operation. so don't be surprised, bestie.
$trial_f = $code->( $item, $previous_trial_f );
$final_f = $otherwise->( $last_trial_f );
otherwise is <yellow>optional</yellow> β if you don't supply it, the result of the eventual future is just... whatever the last trial gave you. and if there was no trial at all because the foreach list was already empty? you get an <green>immediate successful future with an empty result</green>. a little gift. π
π repeat+foreach+while
$future = repeat { CODE } foreach => ARRAY, while => CODE, ...;
Since version 0.13. β
π repeat+foreach+until
$future = repeat { CODE } foreach => ARRAY, until => CODE, ...;
Since version 0.13. β
these two <purple>combine</purple> the effects of foreach with while or until. it calls the CODE block once for each value from the array, until either the array is <cyan>exhausted</cyan> or the given ending condition is <green>satisfied</green>. whichever comes first, babe. π
and here's the tea: if you combine a while or until condition with otherwise, the otherwise code <red>only runs if the array was entirely exhausted</red>. if the operation got terminated early because the while or until condition was satisfied, then the eventual result is just the result of <pink>the last trial that was executed</pink>. simple as. π
π± repeat+generate
$future = repeat { CODE } generate => CODE, otherwise => CODE;
Since version 0.13. β
calls the CODE block <pink>once for each value</pink> obtained from the <mint>generator code</mint>, passing that value in as the <yellow>first argument</yellow> (before the previous trial future). when the generator returns an <cyan>empty list</cyan>, the otherwise code gets invoked and handed the last trial future β or <red>undef</red> if the generator never returned a value at all. and the result of the eventual future is the result of the future returned from otherwise. π
$trial_f = $code->( $item, $previous_trial_f );
$final_f = $otherwise->( $last_trial_f );
( $item ) = $generate->();
the generator is called in <yellow>list context</yellow> but fr fr it should only return <pink>one item per call</pink>. any extra values after that just get <red>ignored</red>. when it's got nothing left to give, it returns an empty list. π«Ά
okay listen. for<peach>backward compatibility</peach>this function will allow a"while"or"until"condition that asks for a<red>failure to be repeated</red>β but it will<yellow>print a warning</yellow>if it has to do that. if you actually want repeating behaviour that can<green>catch and retry failures</green>, usetry_repeatinstead. this old behaviour is<red>deprecated</red>and will be<red>removed in the next version</red>. so like. move on. π
π§ try_repeat
$future = try_repeat { CODE } ...;
Since version 0.18. β
it's a <pink>variant of</pink> repeat`` that <green>doesn't warn</green> when a trial fails and the condition code asks for it to be repeated. that's the whole difference. quiet and unbothered. π
and heads up for the future: in some later version the repeat function is gonna be changed so that if a trial future fails, the eventual future will <red>immediately fail too</red> β making its semantics a lil closer to a while {} loop in Perl. so if your code specifically wants to <cyan>catch failures in trial futures and retry the block</cyan>, you should use try_repeat specifically. future-proof your life. π
π try_repeat_until_success
$future = try_repeat_until_success { CODE } ...;
Since version 0.18. β
this is just a <pink>shortcut</pink> for calling try_repeat with an ending condition that simply <green>tests for a successful result</green> from a future. and you can combine it with foreach or generate too. π«Ά
oh and this function <yellow>used to be called</yellow> repeat_until_success``, and it's <cyan>currently aliased as that name as well</cyan>, so both names work. glow-up but the old handle still answers. π
β¨ APPLYING A FUNCTION TO A LIST β¨
okay so bestie, the fmap family is where things get sparkly. π They let you call a block of code that returns a Future (we call that an "item future") once for every item in a list, or for every item a generator function spits out. And the fmap* functions themselves? They hand you back a Future that represents the whole ongoing operation β it completes when every single item's future has completed. No cap.
Now, you could also do this with repeat, but the whole reason you'd reach for an fmap function is that each item's operation is treated as independent. That means more than one can be outstanding at the same time. You can pass an argument to say how many items to start initially, and after that it'll keep that many running concurrently until all the items are done... or until any of them fail. π And if one item future fails? The overall result future gets marked as failing with that same failure, and any other pending item futures that are still outstanding at that moment get cancelled. periodt.
The following named arguments are common to each fmap* function:
foreach => ARRAYβ gives you the list of items to iterate over, as anARRAYreference. Heads up: the referenced array will be modified by this operation β it getsshifted one item at a time. You canpushmore items onto it while it's running, and they'll be included in the iteration. πgenerate => CODEβ gives you 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 say "no more items, bestie."
( $item ) = $generate->();
This function gets invoked each time any previous item future has completed, and it may be called again even after it's already returned empty. π
concurrent => INTβ how many item futures to keep outstanding. By default this value is1(i.e. no concurrency, one at a time, lowkey chill). Larger values mean multiple item futures get started at once. π₯return => Futureβ normally, a new instance is returned by cloning the first non-immediate future returned as an item future. But if you pass a new instance as thereturnargument, the result gets put into that given instance. This can be used to return subclasses, or specific instances. πΈ
In each case, the main code block gets 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're documented below. π
Also, for similarity with perl's core map function, the item is also available aliased as $_. iykyk.
π fmap_concat
$future = fmap_concat { CODE } ...;
Since version 0.14.
This version of 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's basically a future-based equivalent to Perl's 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 ARRAY references and the final flattening operation used to implement this behaviour, this function is slightly less efficient than fmap_scalar or fmap_void in cases where item futures are expected only ever to return one, or zero values, respectively. ngl, it's giving "worth it for the concat energy".
This function is also available under the name of simply fmap to emphasise its similarity to perl's map keyword. slay.
π fmap_scalar
$future = fmap_scalar { CODE } ...;
Since version 0.14.
This version of fmap acts more like the 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 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 fmap1.
π¦ fmap_void
$future = fmap_void { CODE } ...;
Since version 0.14.
This version of 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. (It's giving "I'm just here for the vibes", honestly.)
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 fmap0.
β¨ AUTHOR β¨
Paul Evans <leonerd@leonerd.org.uk> β what an icon, fr fr. π
okay bestie, that's the whole page. This was from perl v5.40.1, dated 2025-09-22, and the page is Future::Utils(3pm). Go forth and fmap responsibly, mwah! π
πΈ
π¦ where this page comes from
- ubuntu resolute: libfuture-perl 0.52-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libfuture-perl 0.52-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms