Is there any recoverCatching in the Either.catch a...
# arrow
d
Is there any recoverCatching in the Either.catch api (something like in Kotlin's Result, where you can chain a few potentially failing operations and still have a fallback), w/o nesting catches?
s
Could you give an example with Result?
c
It's essentially
leftFlatMap
but I don't know if Arrow has that
d
There's also mapCatching in Result that might be more like leftFlatMap
s
This is
recover
but
recover
is actually more powerful. Normally this would be
handleErrorWith
which is a more common name to
flatMapLeft
, or
handleError
for
mapLeft
. https://apidocs.arrow-kt.io/arrow-core/arrow.core/recover.html,
recover
is the DSL version of both.
Copy code
val original: Either<String, Int> = ...

val a: Either<Other, Int> =
   original.handleErrorWith { msg -> Other(msg).left() }
// original.flatMapLeft { msg -> Other(msg).left() }

val b: Either<Other, Int> =
   original.recover { msg -> raise(Other(msg)) }

val c: Either<Nothing, Int> =
   original.recover { msg -> msg.length } 

val d: Either<Nothing, Int> =
    original.handleError { msg -> msg.length }
If within the DSL I prefer to just use
getOrElse
and
withError
instead of
recover
and
flatMapLeft
though.
Copy code
withError({ msg -> msg.length }) { original.bind() }

msg.getOrElse { msg.length }
d
recover catches Throwables @simon.vergauwen?
s
No, it works the same in the sense that
Result
is bound to
Throwable
and
recover
will allows
raise
inside for the lambda. You could only catch
Throwable
if you're working with
Either<Throwable, A>
, and in that case I guess we could project a
recoverCatching
. I assume you're looking for this because you want to avoid
Result
due to it capturing
CancellationException
?
d
Yes, plus anyways, I'm trying to use Either there to get typed errors for a bigger process... just that operation can be done in multiple ways, and each of them can throw... but in the end, I'd like an Either with the result w/o throwing.
s
Something like this?
Copy code
inline fun <E, A> Either<E, A>.recoverCatching(
  onThrowable: Raise<E>.(Throwable) -> A,
  block: Raise<E>.(E) -> A
): Either<E, A> = when(this) {
  is Right -> this
  is Left -> either {
     catch({ block(value) }, onThrowable)
  }
}
Or return
Either<Throwable, A>
from all your (low-level?) operations, and use:
Copy code
either<E, A> {
   withError({ throwable -> throwable.toError() }) {
      catch({
        `bind` on `Either<E, A>` and `Either<Throwable, A>`
      }) { throwable -> raise(throwable) }
   }
}
With context parameters it can be a DSL.
Copy code
inline fun <E, A> eitherCatching(
   onThrowable: (Throwable) -> E,
   block: context(Raise<E>, Raise<Throwable>) () -> A
): Either<E, A> = either<E, A> {
   withError(onThrowable) {
      catch({
        block()
      }) { throwable -> raise(throwable) }
   }
}
d
Interesting, I think the first option was the closest to my current situation, but they all have a place... I'll give it a try!
👍 1