the other thing about the above syntax that I find...
# arrow
s
the other thing about the above syntax that I find different when I compare to `IO`’s
parTupleN
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
.
@simon.vergauwen would you please give your feedback here ? 🙂
s
Hey @Satyam Agarwal, I think there is a misunderstanding that
parMapN
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.
I hope that clarifies a bit what happened there. The reason you get directly
Tuple2<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>
.
s
Right. Thanks a lot. Also can you help me understand why this syntax looks weird to me when I compare with IO
Copy code
interface 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 :
Copy code
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)
        )
    }
}
@simon.vergauwen
s
The equivalent of the
IO
version is actually the following:
Copy code
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
.
s
Ah, so actually, like you have mentioned multiple times, that suspend encapsulates throwable, but my dense mind wouldn’t get it, I can actually strip away all the Eithers and just use one either at the edge of the world ? But I wanted to convert java’s checked exceptions and use my own sealed class of domain error.
1
s
You can still do that, but since checked exceptions don't exist in Kotlin it's best to do that in local areas. See the pseudo-code below. This will actually force you to deal with your own error domain, rather than exposing
Throwable
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.
Copy code
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)
     }
   }
s
Awesome. Thank you so much @simon.vergauwen By the way team mates are incredibly happy with PR I raise to switch from IO to Either. They said though they were now quite comfortable in working with IO, by doing Either and suspend they understand the concepts better along with the simplicity. They have used Either a lot from Vavr library. Thank you 🙂
👌 1
s
That's awesome! Happy to help 🙂