Объединяя два EitherT, верните первое, если это удастся, иначе верните второе - PullRequest
0 голосов
/ 29 апреля 2020

Рассмотрим следующий фрагмент:

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

Я хочу следующее:

На некотором входе s сначала вызовите foo(s) и, если он "потерпит неудачу", верните вывод bar(s) иначе возвращает вывод foo(s) без вызова bar(s). Я придумал следующее.

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

Есть ли лучший способ сделать то, что я хочу?

Ответы [ 2 ]

1 голос
/ 30 апреля 2020

Вот несколько реализаций

  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

Надеюсь, это поможет

1 голос
/ 29 апреля 2020

Да, recoverWith:

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

Многие другие монады обработки ошибок имеют похожее соглашение: .recover, который принимает тип ошибки к типу успеха, и .recoverWith, который принимает ошибку -типа к целому-монаде типа. Иногда они называются handle и handleWith соответственно. Метод без With всегда требует вычисления чистого значения, а метод с With всегда выполняет вычисления типа монады / оболочки.

...