If understanding this (0.12 !) documentation corre...
# arrow
j
If understanding this (0.12 !) documentation correctly, there is no recommended special (arrow) coroutine builder for starting (for example) an either computation ? https://github.com/arrow-kt/arrow/blob/master/arrow-site/docs/docs/fx/README.md#executing-effectful-programs
s
If you mean:
Copy code
suspend fun example(): Either<String, Int> =
  either {
     val a = Right(1).bind()
     a
  }
Then the answer is no. Since this is 100% agnostic of any coroutine builder. This is true for all
suspend
code you find in Arrow Core. If you're using Arrow Fx that 100% interopts with KotlinX Coroutines out-of-the-box since
0.12.0
there will be no need to pay attention to which coroutine builders you're using anymore. There'll also be no need more anymore to use interopt-libraries from Arrow 🙂
🙌 1
I hope I answered your question correctly but I might've misunderstood your question 😅
👍 1
j
..am still wrapping my head around these guarantees: You wrote earlier...
...And recovering from errors is safe in 
suspend
 even though the code can be async. If you're doing this manually in Java for example you need manually wire exception handling accross async boundaries, here that is done automatically for us through the 
Continuation
I am more thinking about wrapping some database- or http-request with Either, or just using the monad computations. So you're saying that async exception handling is another benefit... ok that's fine, but other than that and monadic computation-blocks, there is no 'runtime stuff' ' that is hadled by arrow in the background, right ? And also you wrote:
...basically every suspend function results in 
A
 or 
Throwable
, that is actually always true in Kotlin since we don't have checked exceptions. However 
suspend
 always takes that into account.
I don't get what is meant by "takes that into account". The exceptions are still handled, if handled at all, by the calling code, right ?
s
ok that's fine, but other than that and monadic computation-blocks, there is no 'runtime stuff' ' that is hadled by arrow in the background, right ?
I'm not 100% sure what you mean by 'runtime stuff'. Computation blocks have their own implementation for running on a
Continuation
. So it has its own 'runtime', or implementation. This is the 'runtime' used for `either { }`, `nullable { }`, etc. These implementations have been worked out in such a way that they automatically integrate with other "runtimes". For example
either { }
works together with KotlinX Coroutines runtime without having a dependency on it. Here is a test that verifies that short-circuiting in `either { }` cancels structured concurrency correctly that is running inside the `either { }` block.
I don't get what is meant by "takes that into account". The exceptions are still handled, if handled at all, by the calling code, right ?
suspend
explicitly models that it will result in
Throwable
or
A
, similar to
IO
.
The exceptions are still handled, if handled at all, by the calling code, right ?
If I understand you correctly, this is a misconception 😄 Let's take
suspend () -> Int
and all we got in the standard library. The only way we can run it is:
Copy code
val f: suspend () -> Int = { 5 }
f.startCoroutine(Continuation(EmptyCoroutineContext) { res: Result<Int> ->
   res.fold({ i: Int -> println(i) }, { e: Throwable -> println(e) })
})
So a
suspend
function can "only" be run by handling both
Int
and
Throwable
. However, KotlinX Coroutines hides this from the user and re-throws unhandled exceptions instead. So depending on which runner you use to run a
suspend
function and an unhandled exception are rethrown.
I am more thinking about wrapping some database- or http-request with Either, or just using the monad computations.
However, that doesn't mean using
Either
in combination with
suspend
doesn't offer a better way to offer your error domain! The power of Arrow and
suspend
in Kotlin is that it can so efficiently mix effects, and you can combine
suspend
with
either { }
without any costs. In contrast to
IO
with
Either
(or rather
EitherT
) which is very user-unfriendly, and performance very costly on the JVM due to all the nesting and wrapping. So I would still advise you to do something like (warning pseudo-code :p):
Copy code
object MyErrorDomain // Some ADT that models some errors 
object User // Some data class

suspend fun DbImpl.fetchUser(id: Int): Either<MyDbError, User> =
  either {
      val queryRes = !Either.catch { query(...) }
      val data = !validated(queryRes)
      !data.toUser()
  }