Open dvisztempacct opened 6 years ago
Yes, I agree we should have these operations available.
I have a gut feeling there's some sort of functional idiom for parallel processing that we can draw from. Maybe @ostera can bring this to light, as he implemented a monad functor as a suggestion for bucklescript?
@gilbert there isn't a particular algebra that models Future values, but if you want map
/bind
you want a monad, and then the runtime would determine how it behaves (in this case, it eagerly runs, and on completion it calls the continuation).
@dvisztempacct the behavior of all
can be achieved by sequencing a list of monads:
module Future = {
type t('a);
let all: (list(t('a)), list('a) => 'b );
};
let sum_results_or_0_if_any_rejects =
[ Future.value(1), Future.value(2), Future.value(3) ]
|> Future.all( ~resolve = List.fold_left((+), 0), ~reject = 0))
/* would essentially be */
let f_1 = Future.value(1);
let f_2 = Future.value(2);
let f_3 = Future.value(3);
let all_3 =
f_1 |. Future.flatMap( first =>
f_2 |. Future.flatMap( second =>
/* at this stage below we have all the values, so we know if any rejections occurred */
/* let's assume they are all okay! */
f_3 |. Future.flatMap( third => Future.value([first, second, third ]))));
let sum_results_or_0_if_any_rejects =
all_3 |. Future.fold( ~reject = _ => 0, ~resolve = List.fold_left((+), 0))
Which can as well come from a similar functor, Traversable 💯
Unfortunately join
(which collides in name with the monadic join) can't be built with variadic parameters, so you'll end up with join
, join3
, join4
, etc. After join4
you start thinking whether you want to just use all
.
Hope this helps 🙏
@ostera
Thanks for the input.
the behavior of all can be achieved by sequencing a list of monads
That's a good suggestion, thanks :)
Please have a look at my PR #10 which implements a facility similar to Bluebird.map()
(which is similar to ES6 Promise.all
but a little more flexible.)
https://github.com/RationalJS/future/pull/10/files
Here is some code to test it:
let sleep : (unit=>unit)=>unit = [%bs.raw "f => setTimeout(f, 1000)"];
let sleepFuture = () => Future.make(resolve => sleep(resolve));
let processItem = (x:int):Future.t(int) => {
Js.log2("processing", x);
sleepFuture() |. Future.map(() => {
Js.log2("completed", x);
x
});
};
let xs = [| 1, 2, 3 |];
xs |. Future.flatMapArray(processItem, 1)
|. Future.get(xs => {
Js.log2("flatMapArray(1) got:", xs);
});
xs |. Future.flatMapArrayUnsafe(processItem, 1)
|. Future.get(xs => {
Js.log2("flatMapArrayUnsafe(1) got:", xs);
});
xs |. Future.flatMapArray(processItem, 2)
|. Future.get(xs => {
Js.log2("flatMapArray(2) got:", xs);
});
xs |. Future.flatMapArrayUnsafe(processItem, 2)
|. Future.get(xs => {
Js.log2("flatMapArrayUnsafe(2) got:", xs);
});
xs |. Future.flatMapArray(processItem, 3)
|. Future.get(xs => {
Js.log2("flatMapArray(3) got:", xs);
});
xs |. Future.flatMapArrayUnsafe(processItem, 3)
|. Future.get(xs => {
Js.log2("flatMapArrayUnsafe(3) got:", xs);
});
I would add something like this to the tests/
dir except the tests seem broken.
the tests seem broken
I've addressed this in PR #18
So now there seems to be PR #22 offering the all
function for list of Futures. 👍 for that ; writing flatMap monad sequences is very clumsy in comparison. However all
is not sufficient in the Reason world because its type:
all: list(t('a)) => t(list('a))
requires a list of homogeneous items -> cannot be used when you want to wait different types of items. Therefore the Js.Promise.all2
, Js.Promise.all3
, etc. supporting tuples of up to 6 items (https://bucklescript.github.io/bucklescript/api/Js.Promise.html)
On that venue I would like to see those tuple versions in this library as well. What do you think?
They're not in the docs, but I discovered map2
, map3
, ... that serve this purpose. You also have to provide a function that maps the results to a tuple....
Future.map3(first, second, third, (a, b, c) => (a, b, c))
This library seems to be lacking idioms for parallel execution.
I'd like to see something like
Promise.all()
and additionally something like Bluebird'sPromise.join()
andPromise.map()
.