What is the best way to run 2-3 different tasks in...
# arrow
m
What is the best way to run 2-3 different tasks in parallell (giving Either as result) with Arrow, i.e. it is not a list of equal operations?
s
• arrow.fx.coroutines.parMapN • either { } (or
parTraverseEither
in
0.12.0
Copy code
suspend fun either(): Either<Int, Long> = TODO()

suspend fun program(): Either<Int, Long> = either {
   parMapN(
     { either().bind() },
     { either().bind() },
     { either().bind() })
     { a, b, c -> a + b + c }
}

suspend fun either2(i: Int): Either<Int, Long> = TODO()

suspend fun program2(): Either<Int, List<Long>> =
  listOf(1, 2, 3).parTraverseEither { i -> either2(i) }
m
Thought of the first. Need to make an artificial list then, or maybe Pair. either {}, that is sequencially?
s
You can safely call
bind()
from concurrent contexts
So
parMapN
takes care of calling the functions in parallel, and you can use
bind()
safely from those parallel running functions.
It automatically takes into account cancellation and everything 🙂 So if one of the function returns
Left
and the
bind
short-circuits it first cancels the other tasks before returning
Left
from the
either
block
That is possible since we can mix effects with
suspend
Where here we mix the
IO
effect with
Either
effect which effectively makes it a
EitherT
and
IO
stack without any of the monad transformers costs
m
Hmm, implicit IO makes this a bit hard to reason about. How would the first example be without either {} and bind()? Would be nice to have some of those examples added to the documentation/website.
s
Contributions to docs are very welcome 😉 We're currently focusing on getting the release that streamlines the library for 1.0.
If you don't use
either { }
and
bind()
in the first example you could do this but it is semantically not the same.
Copy code
suspend fun either(): Either<Int, Long> = TODO()

suspend fun program(): Either<Int, Long> = 
   parMapN(
     { either() },
     { either() },
     { either() })
     { a: Either<Int, Long>, b, c ->
       a.flatMap { aa -> b.flatMap { bb -> c.map { cc -> aa + bb + cc } } }
     }
The difference there is that all 3 functions will always run to finish, even if a
Left
was encountered. Whilst if you call
bind()
inside the
parMapN
functions it will short-circuit the parallel operations as well.
m
Ah sorry, I was reading parTraverseN instead of parMapN here 😄 Then it makes more sense, but either {} outside all confuses me when there is no similar to for comprehension.
s
Yes, understandable 😄 But
either { }
is totally different from for comprehension.
It's much more flexible, and is actually faster than
flatMap
afaik
m
Sure about
either().bind()
(not tested yet)? I would think the value would be Long and not
Kind<F, A>
s
Yes, you get back
Long
from
either().bind()
but there is multiple different examples now here in the thread so not sure which one you talking about 😅
m
I refer to the first example and parMapN now 🙂
s
Ah yes, there is a mistake in there sorry.
Copy code
suspend fun either(): Either<Int, Long> = TODO()

suspend fun program(): Either<Int, Long> = either {
   parMapN(
     { either().bind() },
     { either().bind() },
     { either().bind() })
     { a, b, c -> a + b + c }
}
There was a
bind()
at the end of
parMapN
but that's not necessary since
parMapN
just returns
Long
here. Which is lifted by the
either { }
block back into
Either<Int, Long>
I think that was your question, right?
m
Well, no, the other bind()s. Have you tried compiling it? I would think Kind<F, A> would not accept a Long?