rednifre
03/14/2023, 6:31 PMsimon.vergauwen
03/14/2023, 6:49 PMalpha.
either { } required suspend because it co-operates with Kotlin Coroutines. When it short-circuits on Either.Left.bind it needs to cancel the Kotlin Coroutines it's potentially running inside of.
To avoid requiring suspend when not running inside a Kotlin Coroutine you had to use either.eager.
This is now solved by working more the inline behavior of Kotlin, which automatically works with suspend and inlines the same behavior as before into a Kotlin Coroutine when used inside of one.simon.vergauwen
03/14/2023, 6:50 PMeither { } and either.eager { } the new DSL is source compatible with the current one. So only requires changing package.rednifre
03/14/2023, 7:17 PMsimon.vergauwen
03/14/2023, 7:24 PMError2 you can create a common sealed interface and since either and Either are covariant in E you can do.
sealed class CommonError
object Error1 : CommonError
object Error2: CommonError
val x: Either<Error1, Int> = 1.right()
val y: Either<Error2, Int> = 1.right()
val sum = either.eager {
x.bind() + y.bind()
}
(Drop eager for alpha version of Arrow)
If you don't control the errors:
• You can apply the same pattern but wrapping the original errors
• Resort to Either<Either<Error1, Error2>, Result>
With context receivers it's quite neat, but sadly those are still only experimental for JVM. In that case you can do something neat.
object Error1 : CommonError
object Error2: CommonError
val x: Either<Error1, Int> = 1.right() // fun Raise<Error1>.x(): Int = 1
val y: Either<Error2, Int> = 1.right() // fun Raise<Error1>.y(): Int = 1
context(Raise<Error1>, Raise<Error2>)
fun sum(): Int =
x.bind() + y.bind() // x() + y()
context(Raise<Error2>)
fun resolveError1(): Int =
recover({ sum() }) { err: Error1 -> 0 }simon.vergauwen
03/14/2023, 7:26 PMval sum: Either<Error1 | Error2, Int> = either {
x.bind() + y.bind()
}
context(Raise<Error1 | Error2>)
fun sum(): Int = x() + y()rednifre
03/14/2023, 7:30 PMsealed interface Error1_2
sealed interface Error2_3
object Error1 : Error1_2
object Error2 : Error1_2, Error2_3
object Error3 : Error2_3
fun a(): Either<Error1_2, String>
fun b(): Either<Error2_3, String>
or what would be a good solution in that case?simon.vergauwen
03/14/2023, 7:32 PMDomainError, and some smaller sealed interface that inherit from the parent.
The errors of a layer form a sealed interface and all the layers inherit from the top-most parent. That allows all errors to be combined conveniently as a travel through the layers.rednifre
03/14/2023, 7:34 PMsimon.vergauwen
03/14/2023, 7:39 PMUnexpected all together from this hierachy.simon.vergauwen
03/14/2023, 7:40 PMDomainError or be removed all together. It's something I've used here to not throw any exceptions and then map to 500 but it should probably just stay an exception instead.
More details in this discussion: https://kotlinlang.slack.com/archives/C5UPMM0A0/p1678805582480059?thread_ts=1678801512.268339&cid=C5UPMM0A0rednifre
03/14/2023, 7:40 PMsimon.vergauwen
03/14/2023, 7:41 PM