https://kotlinlang.org logo
#arrow
Title
# arrow
s

Sam Pengilly

01/27/2023, 5:15 AM
Could someone confirm my assumptions about combining `effect {}`/`either {}` with exception throwing code. I have a setup like this:
Copy code
suspend fun <T> someFunc(
    retryPolicy: Schedule<...>, 
    parse: EffectScope<Throwable>.(res: Response) -> T
) = either {
    val response = retryPolicy.retry { someThrowingCode() }
    parse(response)
}
I would expect that any
NonFatal
exception raised within the
either {}
block would get captured by the underlying
DefaultEffect
and recovered to a
Left
. However in a unit test to confirm this behaviour the exception is raised to the test itself and fails the test before
someFunc
returns
Is this where
attempt {} catch {}
comes in? I’m just now seeing that
seems that this works as expected, is this the correct approach?
Copy code
suspend fun someFunc(...) = either {
    val a = runCatching { someThrowingCode() }.bind { it }
    // do something with a
}
Using
attempt {} catch {}
and manually calling
shift()
didn’t seem to change anything
same effect using
Either.catch {}
in place of
runCatching {}
s

Stylianos Gakis

01/27/2023, 8:45 AM
Not sure about the rest, but runCatching will swallow both cancellation exception from coroutines, and if you call shift inside there afaik, since it catches everything. I'd guess you don't want to use that.
s

Sam Pengilly

01/27/2023, 8:55 AM
good point. It just seems a bit strange in general. Since
either {}
is just an alias for
effect {}.toEither()
I would have thought that the underlying functionality would automatically capture an exception thrown in the effect as
Left
in the
toEither()
call
s

simon.vergauwen

01/27/2023, 9:00 AM
Hey @Sam Pengilly,
Throwable
is not automatically captured by
EffectScope<Throwable>
you need to explicitly capture it. You're best bet is to use
Either.catch { }
and then call
bind
on it.
runCatching
vs
Either.catch
the latter doesn't capture
Fatal
exceptions or
CancellationException
. This is because
Result
was build for
Continuation
internals, and
Either
was build for application development. So you probably want to wrap
retryPolicy.retry
in
Either.catch
here.
attempt { } catch { }
exists, but that works over typed errors only atm. We're still designing DSL shaped operators for this in 2.x.x. I am hoping to backport them to 1.2.x soon, and share a PR here for gathering feedback. Here is the design in 2.x.x. All functions from this point and below, https://github.com/arrow-kt/arrow/blob/arrow-2/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Raise.kt#L211. Any earlier feedback would be greatly appreciated 🙏 Hope this helps ☺️
s

Sam Pengilly

01/27/2023, 9:05 AM
so from looking at some of those recover signatures, the aim in 2.x.x is to have either any typed errors that are `raise`'d or any exceptions that are `throw`n be picked up by the
Effect
automatically? I was thrown off today as it seemed like the signatures for this were already there in 1.x, with
fold
being used and
DefaultEffect
having a
catch(Throwable) {}
block internally
plus there was some logic for dealing with NonFatal vs Fatal exceptions in there
s

simon.vergauwen

01/27/2023, 9:07 AM
That to allow recovering from unexpected exceptions inside of
Effect.fold
when consuming it at the edge. So when calling
fold
on
Effect<E, A>
you can recover to
B
from
E
,
Throwable
and
A
.
10 Views