Alevinevich
Alevinevich

Reputation: 43

How to call Arrow.kt function returning basically Either but using multiple context receivers

Arrow.kt docs point out that instead of:

fun get(): Either<Err, Res>

we can use

context(Raise<Err>)
fun get(): Res

And we can even have multiple context receivers.

Imagine we have two error types

class Err1
class Err2

And a function that has two context receivers of those errors

context(Raise<Err1>, Raise<Err2>)
private fun func(a: Int): Int {
    return when(a) {
        0 -> raise(Err1())
        1 -> raise(Err2())
        else -> a
    }
}

How this function can be elegantly called to handle many or all errors without deep nesting?

Only ugly nesting way comes to my mind:

recover<Err1, Unit>({
    recover<Err2, Unit>({
        val res = func(1)
        //happy pass
    }) { err2 -> /* error 1 */ }
}){ err1 -> /* error 2 */ }

I can remove nesting by creating separate functions and recovering only once per function but that arguably would be even worse.

P.S. Arrow.kt with context receivers awfully reminds me how checked exceptions work in java. Except that your error is not required to inherit Throwable and the fact that all exceptions can be caught much cleaner.
That's a bit funny that we came a full circle from Java checked exceptions to Kotlin's no checked exceptions to Arrow.kt way to 'emulate' checked exceptions with context receivers :)

Upvotes: 1

Views: 931

Answers (1)

vitorscoelho
vitorscoelho

Reputation: 1

As Agusto mentioned, try using "sealed interfaces".

So:

sealed interface Err {
    class Err1 : Err
    class Err2 : Err
}

context(Raise<Err>)
private fun func(a: Int): Int {
    return when (a) {
        0 -> raise(Err.Err1())
        1 -> raise(Err.Err2())
        else -> a
    }
}

And:

recover<Err, Unit>({
    val res = func(1)
    //happy pass
}) { err ->
    when (err) {
        is Err.Err1 -> /* error 1 */
        is Err.Err2 -> /* error 2 */
    }
}

Personally, for "func", I prefer:

context(Raise<Err>)
private fun func(a: Int): Int {
    ensure(a != 0) { Err.Err1() }
    ensure(a != 1) { Err.Err2() }
    return a
}

Upvotes: 0

Related Questions