Satyam Agarwal
09/14/2020, 8:45 AMparTupleN
is why this method exists again ? In IO
, parMapN
was deprecated in favor of parTupledN
because the returned type was not aligned with returned type of other apis in the library.
If I use parTupleN
from arrow-fx-coroutines
, I was expecting that if I am giving it 2 methods with Either
return types, it should give me Either<Throwable, Tuple2<A, B>>
, instead it gives me Tuple2<A, B>, which then needs to be producted like I am doing above with parMapN
.Satyam Agarwal
09/15/2020, 2:57 PMsimon.vergauwen
09/16/2020, 7:35 AMparMapN
was deprecated in IO
for parTupledN
? In IO
we deprecated `parMapN`'s f: (A, B) -> C
in favor of f: (Tuple2<A, B>) -> C
because that's how it works in Applicative
, due to how it's derived from other combinators in interface Applicative
. So we aligned the signatures between Concurrent#parMapN
& Applicative#mapN
.
However in Arrow Fx Coroutines since it's a concrete implementation for suspend
we don't have that limitation of deriving from Applicative
, so for that reason, we have gone back to the simpler (and slightly more efficient) f: (A, B) -> C
signature. So that you can directly apply function references without having to mangle the TupleN
types manually in between.simon.vergauwen
09/16/2020, 7:36 AMTuple2<A, B>
instead of Either<Throwable, Tuple2<A, B>>
is because Throwable
is encapsulated in suspend
just like it was in IO
so instead of IO<Tuple2<A, B>>
you now get suspend () -> Tuple2<A, B>
.Satyam Agarwal
09/16/2020, 8:04 AMinterface SomeRepo {
suspend fun getId(someId: Int): Either<Throwable, Int>
suspend fun getName(someName: String): Either<Throwable, String>
}
interface AnotherClass {
val someRepo: SomeRepo
suspend fun <A, B> getResult(someName: String, someId: Int): Either<Throwable, Tuple2<Int, String>> {
return parMapN(
suspend { someRepo.getId(someId) },
suspend { someRepo.getName(someName) }
) { a, b -> a.product { b } }
}
}
When I write this with IO :
interface SomeRepo {
fun getId(someId: Int): IO<Int>
fun getName(someName: String): IO<String>
}
interface AnotherClass {
val someRepo: SomeRepo
fun <A, B> getResult(someName: String, someId: Int): IO<Tuple2<Int, String>> {
return IO.parTupledN(
someRepo.getId(someId),
someRepo.getName(someName)
)
}
}
Satyam Agarwal
09/16/2020, 8:07 AMsimon.vergauwen
09/16/2020, 11:14 AMIO
version is actually the following:
interface SomeRepo {
suspend fun getId(someId: Int): Int
suspend fun getName(someName: String): String
}
interface AnotherClass {
val someRepo: SomeRepo
suspend fun <A, B> getResult(someName: String, someId: Int): Tuple2<Int, String> =
parTupledN(
{ someRepo.getId(someId) },
{ someRepo.getName(someName) }
)
}
If you want the Throwable
to Either
you can always use Either.catch
at any point in time like you would do IO#attempt
.Satyam Agarwal
09/16/2020, 11:42 AMsimon.vergauwen
09/16/2020, 12:36 PMThrowable
in Either
all over the place. Other unexpected exceptions will still be correctly encapsulated in suspend
just like IO
, aallowing you to safely ignoring them until the edge of the world.
sealed class MyError {
data class Unexcepted(val throwable: Throwable): MyError()
object FailedToRead : MyError()
}
suspend fun someBlockingJavaApi(): Either<MyError, Result> =
Either.catch {
val res = evalOn(IOPool) { javaCall() }
}.mapLeft { exception ->
when(exception) {
is ExpectedCheckedException -> FailedToRead
else -> Unexcepted(exception)
}
}
Satyam Agarwal
09/16/2020, 12:57 PMsimon.vergauwen
09/16/2020, 2:05 PM