0

Consider the following snippet:

def foo(x:String): EitherT[F, Throwable, String] = ???
def bar(x:String): EitherT[F, Throwable, String] = ???

I want the following:

On some input s, first call foo(s) and if it "fails" return the output of bar(s) else return the output of foo(s) without calling bar(s). I have come up with the following.

def foobar(s:String) = {
  val f = foo(s)
  // if f is successful return f else return bar(s)
  f.biflatMap(_ => bar(s), _ => f)
}

Is there a better way to do what I want?

Jus12
  • 17,824
  • 28
  • 99
  • 157

2 Answers2

2

Yes, recoverWith:

foo(s).recoverWith { _ =>
 bar(s)
}

Many other error-handling monads have similar convention: .recover that takes error-type-to-success-type, and .recoverWith that takes error-type-to-whole-monad-type. Sometimes they are named handle and handleWith respectively. Method without With always takes pure value calculation, and method with With always takes monad/wrapper type computation.

Mateusz Kubuszok
  • 24,995
  • 4
  • 42
  • 64
  • Looks much better and works fine with Scala 2.13 https://scastie.scala-lang.org/X24pfKa1Qqy7gyTjzz2LLg but gives an error with Scala <= 2.12: https://scastie.scala-lang.org/oTLzs9BLRmaLHZGZdCoV7w . The error is `type mismatch; found : Throwable => cats.data.EitherT[scala.concurrent.Future,Throwable,String] required: PartialFunction[Throwable,cats.data.EitherT[scala.concurrent.Future,Throwable,String]]` – Jus12 Apr 29 '20 at 18:02
  • 2
    For 2.12 and before you have to make it a partial function explicitly - replace `_` with `case _: Throwable` and you are good to go. – Mateusz Kubuszok Apr 29 '20 at 18:03
  • https://scastie.scala-lang.org/kXhiVpQxQbC9DquHiMht7A making partial function also seems to give an issue with older scala versions. The new error is `could not find implicit value for parameter F: cats.Monad[scala.concurrent.Future] not enough arguments for method recoverWith: (implicit F: cats.Monad[scala.concurrent.Future])cats.data.EitherT[scala.concurrent.Future,Throwable,String]. Unspecified value parameter F.` :) – Jus12 Apr 29 '20 at 18:10
  • You are missing `implicit def ec: scala.concurrent.ExecutionContext` - see https://github.com/typelevel/cats/blob/f44f25136260d6e7e35c21ab4c1aaacc6b2fa7a2/core/src/main/scala/cats/instances/future.scala#L10 – Mateusz Kubuszok Apr 29 '20 at 18:21
1

Here's some implementations

  import cats.implicits._
  import import cats.data.EitherT


  foo("").biflatMap(
    err => bar(""),
    str => EitherT.fromEither[F](str.asRight[Throwable])
  )

  foo("").biflatMap(
    err => bar(""),
    str => EitherT.liftF(str.pure[F])
  )

  foo("").leftFlatMap(
    err => bar("")
  )

  foo("").recoverWith(err => bar(""))

  for {
    err <- foo("")
    x <- bar("")
  } yield x

I hope this helps

Rhys Bradbury
  • 1,699
  • 13
  • 24