代码之家  ›  专栏  ›  技术社区  ›  expert

如何将其异常包装到左边?

  •  0
  • expert  · 技术社区  · 6 年前

    OptionT[IO, Value] 这样地

    case class FailureMsg(code: String, ex: Option[Throwable])
    
    val fff: IO[Either[FailureMsg, Int]] = OptionT.some[IO](12345)
      .map { value ⇒
        println("Mapping over")
        value
      }
      .flatMapF[Int](_ ⇒ IO.raiseError(new RuntimeException("err1")))
      .toRight(FailureMsg("Code0", None))
      .recoverWith {
        case ex ⇒ // Not Throwable!
          EitherT.leftT[IO, Int](FailureMsg("Code1", Some(ex)))
      }
      .value
    

    我怎样才能抓住你 err1 Left[FailureMsg] . 我预料到了 recoverWith 帮帮我,但令人惊讶的是它的别名 mapLeft . 我该怎么办?

    2 回复  |  直到 6 年前
        1
  •  0
  •   expert    6 年前

    我写了helper类来做这个。

    implicit class EitherTExt[F[_], A, B](val obj: EitherT[F, A, B]) {
        def recoverThrowable(pf: PartialFunction[Throwable, Either[A, B]])(implicit A: ApplicativeError[F, Throwable]): EitherT[F, A, B] =
          EitherT(obj.value.recover(pf))
      }
    

    如果有更优雅的短途路线,请告诉我。

        2
  •  0
  •   muradm    6 年前

    我会跟着类型走。

    val start: OptionT[IO, Int] = OptionT.some[IO](12345)
    
    val thenMap: OptionT[IO, Int] = start.map { value ⇒
      println("Mapping over")
      value
    }
    
    // here it will get off the rails
    val thenFlatMapF: OptionT[IO, Int] =
      thenMap.flatMapF[Int](_ ⇒ IO.raiseError(new RuntimeException("err1")))
    
    val thenToRight: EitherT[IO, FailureMsg, Int] =
      thenFlatMapF.toRight(FailureMsg("Code0", None))
    
    val result: IO[Either[FailureMsg, Int]] = thenToRight.value
    

    thenFlatMapF 不会产生 OptionT[IO, Int] IO.raiseError 是这样的,因为没有 Throwable 为了什么?在折叠结果中会出现异常 .

    val thenFlatMapF: OptionT[IO, Int] = thenMap.flatMapF[Int](_ ⇒ {
      IO.raiseError[Option[Int]](new RuntimeException("err1")).recoverWith {
        case err =>
          val result: Option[Int] = ???
          IO.pure(result)
      }
    })
    

    如何在不中断的情况下就地处理错误 IO Option 以便 是生产出来的吗?

    flatMapF 如果失败了,需要关于错误的信息,那么最好是 EitherT 作为它的容器,而不是 OptionT .

    一旦完成,可能的解决方案表明,在某一点上 leftMap 或者应该做方差来映射 可抛出 FailureMsg . 原因之一是 可抛出 . 不能混为一谈 可抛出 . 任何一个继承都是必需的,以便 属于类型 Throwable/Exception ,或应在适当位置绘制错误。

    我的大致解决方案是:

    val fff: IO[Either[FailureMsg, Int]] = OptionT.some[IO](12345)
      // ok, let's do some input interpretation
      .map { value ⇒
        println("Mapping over")
        value
      }
      // if no input, means error
      .toRight(FailureMsg("Code0", None))
      // if there is interpreted input to process, do it and map the errors
      .flatMapF(_ ⇒ IO.raiseError[Int](new RuntimeException("err1")).attempt.map(_.leftMap {
        case err: RuntimeException if err.getMessage == "err1" => FailureMsg("err1", Some(err))
        // just for illustration, that if you have temptation to map on error,
        // most likely there won't be only exception
        case t: Throwable => FailureMsg("unexpected", Some(t))
      }))
      .value
    

    但是,通常 平面地图 将是包含错误处理的单独函数或效果。像这样:

    val doJob: Int => IO[Either[FailureMsg, Int]] = arg =>
      IO.raiseError[Int](new RuntimeException("err1")).attempt.map(_.leftMap {
        case err: RuntimeException if err.getMessage == "err1" => FailureMsg("err1", Some(err))
        case t: Throwable => FailureMsg("unexpected", Some(t))
      })
    
    val fff: IO[Either[FailureMsg, Int]] = OptionT.some[IO](12345)
      .map { value ⇒
        println("Mapping over")
        value
      }
      .toRight(FailureMsg("Code0", None))
      .flatMapF(arg ⇒ doJob(arg))
      .value