I'm mulling over something regarding suspend
that Arrow's documentation explains in detail: suspend () -> A
offers the same guaranties as IO<A>
.
So, according to the documentation, just using suspend
we are converting our impure functions into pure functions:
Impure
fun log(message: String): Unit = println(message)
fun main(): Unit {
log("Hey!")
}
Pure
suspend fun log(message: String): Unit = println(message)
fun main(): Unit = runBlocking {
log("Hey!")
}
The fact that just adding suspend
turns the function into pure was surprising but was clearly explained in the doc.
Considering this, my next doubt is related to the modelling of business services that could result in an error (Throwable
) or in a value A
.
Up to now I was doing something like this:
suspend fun log(message: String): Either<Throwable, Unit> = either { println(message) }
suspend fun add(sum1: Int, sum2: Int): Either<Throwable, Int> = either { sum1 + sum2 }
suspend fun main() {
val program = either<Throwable, Unit> {
val sum = add(1, 2).bind()
log("Result $sum").bind()
}
when(program) {
is Either.Left -> throw program.value
is Either.Right -> println("End")
}
}
BUT, given that suspend fun fn() : A
is pure and equivalent to IO<A>
, we could rewrite the above program as:
suspend fun add(sum1: Int, sum2: Int): Int = sum1 + sum2
suspend fun log(message: String): Unit = println(message)
fun main() = runBlocking {
try {
val sum = add(1, 2)
log("Result $sum")
} catch( ex: Throwable) {
throw ex
}
}
Is there any reason to prefer suspend fun fn(...): Either<Throwable, A>
over suspend fun fn(...): A
?