Arrow: Either.catch {f} instead of Try { f }

Created on 30 Sep 2019  路  5Comments  路  Source: arrow-kt/arrow

Since version 0.10.0 Try { f } is marked as deprecated and it should be replaced either with Either.catch { f} or by using the IO monad.

Before that change we could use Try to do something like that Try { f() } where f() is some simple function that may throw an exception(like valueOf from Enum) and work over the result of Try.
After the release 0.10.0 this call should be replaced with Either.catch { f }. That would not be a problem as the change from Try to Either is obvious as Try is kind of Either although function catch in Either is marked as suspended and it is required by Kotlin to run it in coroutine scope causing to write something like that

runBlocking {
  Either.catch { f }
    ...
}

or by calling it from another suspended function which is not required in my scope. Also I don't think using IO is a solution for handling some simple try catch scenario.

How this kind of problem should be now solved in Arrow:0.10.0 with runBlocking or any other way around?

Most helpful comment

tl;dr add this to your codebase:

fun <A> Either<Throwable, A>.unsafeCatch(f: () -> A) = 
  try { f().right() } catch (t: Throwable) { t.left() }

As a library we're aiming to only provide safe constructs, things that don't behave in unexpected ways or rely on side-effects. Everything that falls in that category is wrapped on suspend or IO. One example is that Try { } could catch an unintended cancellation exception.

We're not against having impure functions that provide the same value, and in the future we may provide arrow-unsafe for this. For now the responsibility for handling these correctly falls on your project :D

All 5 comments

tl;dr add this to your codebase:

fun <A> Either<Throwable, A>.unsafeCatch(f: () -> A) = 
  try { f().right() } catch (t: Throwable) { t.left() }

As a library we're aiming to only provide safe constructs, things that don't behave in unexpected ways or rely on side-effects. Everything that falls in that category is wrapped on suspend or IO. One example is that Try { } could catch an unintended cancellation exception.

We're not against having impure functions that provide the same value, and in the future we may provide arrow-unsafe for this. For now the responsibility for handling these correctly falls on your project :D

Thanks for answer I thought it will end up in new extension function.

tl;dr add this to your codebase:

fun <A> Either<Throwable, A>.unsafeCatch(f: () -> A) = 
  try { f().right() } catch (t: Throwable) { t.left() }

As a library we're aiming to only provide safe constructs, things that don't behave in unexpected ways or rely on side-effects. Everything that falls in that category is wrapped on suspend or IO. One example is that Try { } could catch an unintended cancellation exception.

We're not against having impure functions that provide the same value, and in the future we may provide arrow-unsafe for this. For now the responsibility for handling these correctly falls on your project :D

Thanks for that helpful answer.

I just checked the arrow code and wonder if this small change would be better?

fun <A> Either<Throwable, A>.unsafeCatch(f: () -> A) = 
  try { f().right() } catch (t: Throwable) { t.nonFatalOrThrow().left() }

Then, fatal errors would not be caught.

Yes indeed, that snippet would indeed be safer to use!

I think the unsafeCatch should be an extension to the Either.Companion, not to the Either<Throwable, A>:

fun <A> Either.Companion.unsafeCatch(f: () -> A): Either<Throwable, A> =
        try { f().right() } catch (t: Throwable) { t.nonFatalOrThrow().left() }
Was this page helpful?
0 / 5 - 0 ratings

Related issues

raulraja picture raulraja  路  5Comments

JLLeitschuh picture JLLeitschuh  路  4Comments

Northburns picture Northburns  路  4Comments

jmfayard picture jmfayard  路  4Comments

vejeta picture vejeta  路  3Comments