将潜在的未来恢复到猫的EitherT的左手中?

时间:2019-02-28 17:30:44

标签: scala scala-cats

如果我有一个decoder_input= Input(shape_equal_to_encoder_output_shape) decoder = Conv2D(32, (3, 3), activation='relu', padding='same')(decoder_input) x = UpSampling2D((2, 2))(decoder) x = Conv2D(32, (3, 3), activation='relu', padding='same')(x) x = UpSampling2D((2, 2))(x) decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x) decoder = Model(decoder_input, decoded) 表示可能的错误消息(auto_input = Input(shape=(28,28,1)) encoded = encoder(auto_input) decoded = decoder(encoded) auto_encoder = Model(auto_input, decoded) )或成功的计算(Future[Either[String, Int]]),则移动String'很简单出现在左侧的错误消息:

Int

我希望Future存在类似的东西,但是也许我只是找不到它叫什么。它相对简单,但是需要拆箱然后重新装箱,感觉很不舒服。

def handleFailure(fe: Future[Either[String,Int]]) =
  f.recover({ case e: Exception => Left(s"failed because ${e.getMessage}"))

Cat确实在while ago中添加了一个EitherT实例,但是它专门用于直接恢复到Either的def handleFailureT(fe: EitherT[Future, String, Int]) = EitherT(handleFailure(et.value)) // See above for handleFailure definition 中,而不是替换Either本身。

MonadError是否实现了Cats,如果这样,它叫什么名字?

3 个答案:

答案 0 :(得分:3)

这一点都不明显,但是我认为这就是attemptattemptT的目的。例如:

val myTry: Try[Int] = Try(2)
val myFuture: Future[String] = Future.failed(new Exception())
val myTryET: EitherT[Try, Throwable, Int] = myTry.attemptT
val myFutureET: EitherT[Future, Throwable, String] = myFuture.attemptT

// alternatively
val myFutureET: EitherT[Future, Throwable, String] = EitherT(myFuture.attempt)

有一个PR可以将其添加到文档中:https://github.com/typelevel/cats/pull/3178 -但是它目前没有出现在文档中。但是,您可以在这里看到它:https://github.com/typelevel/cats/blob/master/docs/src/main/tut/datatypes/eithert.md#from-applicativeerrorf-e-to-eithertf-e-a

答案 1 :(得分:0)

在此上花费了几个小时后,我可以肯定的是,截至2019年3月,此功能尚未直接在cat中实现。但是,已经存在的catsDataMonadErrorFForEitherT monad确实可以以主要简单的方式实现它。

implicit class EitherTFutureAdditions[A, B](et: EitherT[Future, A, B]) {
  val me = EitherT.catsDataMonadErrorFForEitherT[Future, Throwable, A]

  def recoverLeft(pf: PartialFunction[Throwable, A]): EitherT[Future, A, B] =
    me.recoverWith[B](et) { case t: Throwable =>
      EitherT.fromEither[Future](Left(pf(t)))
    }
}

我不确定在泛型隐式类中构造monad的性能含义如何,但它确实有效。如果您不需要一般情况,则可以将[A, B]替换为显式类型。

我当时也写了recoverWithFlathandleErrorLefthandleErrorWithFlat并将它们打包成文件EitherTUtils.scala

// Place this in a new file and then use it like so:
//
//   import EitherTUtils.EitherTFutureAdditions
//
//   val et: EitherT[Future, String, Int] =
//     EitherT(Future.failed[Either[String, Int]](new Exception("example")))
//   et recoverLeft {
//     case e: Exception => s"Failed with reason ${e.getMessage}"
//   }
//
object EitherTUtils {

  /**
    * Convenience additions for recovering and handling Future.failed within an EitherT
    *
    * @see [[cats.ApplicativeError]] for recover, recoverWith, handleError, handleErrorWith, and attemptT
    *
    * @param et a Futured EitherT
    * @tparam A the Either's left type
    * @tparam B the Either's right type
    */
  implicit class EitherTFutureAdditions[A, B](et: EitherT[Future, A, B]) {
    val me = EitherT.catsDataMonadErrorFForEitherT[Future, Throwable, A]

    /**
      * Recover from certain errors from this EitherT's Future (if failed) by mapping them to the EitherT's
      * left value.
      *
      * @see [[recoverWithFlat]] for mapping to an Either[Future, A, B]
      *
      * @see [[handleErrorWithFlat]] to handle any/all errors.
      */
    def recoverLeft(pf: PartialFunction[Throwable, A]): EitherT[Future, A, B] =
      me.recoverWith[B](et) {
        case t: Throwable =>
          EitherT.fromEither[Future](Left(pf(t)))
      }

    /**
      * Recover from certain errors from this EitherT's Future (if failed) by mapping them to the EitherT's
      * value.
      *
      * @see [[recoverLeft]] for mapping to an EitherT's left value.
      *
      * @see [[handleErrorWithFlat]] to handle any/all errors.
      */
    def recoverWithFlat(pf: PartialFunction[Throwable, Either[A, B]]): EitherT[Future, A, B] =
      me.recoverWith[B](et) {
        case t: Throwable =>
          EitherT.fromEither[Future](pf(t))
      }

    /**
      * Handle any error from this EitherT's Future (if failed) by mapping them to the EitherT's left value.
      *
      * @see [[recoverWithFlat]] for handling only certain errors
      *
      * @see [[handleErrorLeft]] for mapping to the EitherT's left value
      */
    def handleErrorLeft(pf: PartialFunction[Throwable, A]): EitherT[Future, A, B] =
      me.handleErrorWith[B](et) { t =>
        EitherT.fromEither[Future](Left[A, B](pf(t)))
      }

    /**
      * Handle any error from this EitherT's Future (if failed) by mapping them to the EitherT's value.
      *
      * @see [[recoverWithFlat]] for handling only certain errors
      *
      * @see [[handleErrorLeft]] for mapping to the EitherT's left value
      */
    def handleErrorWithFlat(pf: PartialFunction[Throwable, Either[A, B]]): EitherT[Future, A, B] =
      me.handleErrorWith[B](et) { t =>
        EitherT.fromEither[Future](pf(t))
      }
  }
}

我认为这些可能是我对猫的第一笔贡献,但是在浏览图书馆布局几小时后,我意识到所做的修改并不容易,而且我尚不具备以< 不需要其他项目贡献者的大量工作。

一旦我更好地了解了cats库的结构,我可能会再试一次。

答案 2 :(得分:0)

这是您的EitherTUtils的通用版本:

import cats.data.EitherT

object EitherTUtils {

  implicit class EitherTRecoverErrors[F[_], A, B, E](et: EitherT[F, A, B])(implicit me: MonadError[F, E]) {
    type FE[X] = EitherT[F, A, X]
    implicit val ME: MonadError[FE, E] = implicitly

    def recoverLeft(pf: PartialFunction[E, A]): EitherT[F, A, B] =
      ME.recoverWith(et)(pf.andThen(EitherT.leftT(_)))

    def recoverWithFlat(pf: PartialFunction[E, Either[A, B]]): EitherT[F, A, B] =
      ME.recoverWith(et)(pf.andThen(EitherT.fromEither(_)))

    def handleErrorLeft(f: E => A): EitherT[F, A, B] =
      ME.handleErrorWith(et)(f.andThen(EitherT.leftT(_)))

    def handleErrorWithFlat(f: E => Either[A, B]): EitherT[F, A, B] =
      ME.handleErrorWith(et)(f.andThen(EitherT.fromEither(_)))
  }

}

object Usage {
  import EitherTUtils._
  import cats.implicits._

  import scala.concurrent.ExecutionContext.Implicits.global

  val e: EitherT[Future, String, Int] = EitherT.liftF(Future.failed(new RuntimeException)).recoverLeft {
    case e: IllegalStateException =>
      e.getMessage
  }

}

我同意猫可以使与“失败的” EitherT一起使用变得更加容易,希望我们在将来的版本中会看到类似的东西。