如何将 EitherT 的异常包装到左侧?
How do I wrap exception of EitherT into left?
假设我有 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
的别名。我该怎么办?
我写了帮助程序 class 来做这件事。
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))
}
让我知道是否有更简洁的方法。
我会遵循类型。
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
如果 IO.raiseError
是这种情况,thenFlatMapF
不会产生 OptionT[IO, Int]
,因为 Throwable
没有默认映射到什么? IO.raiseError
.
的折叠结果会出现异常
第一次尝试修复它,将说明它:
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
和 return Option
的情况下处理错误,以便生成 OptionT[IO, Int]
?
所以基本上,在这种情况下,如果您希望 flatMapF
失败并需要有关错误的信息,那么最好将 EitherT
作为其容器,而不是 OptionT
.
完成后,可能的解决方案表明,在某个点 leftMap
或应执行方差以将 Throwable
映射到 FailureMsg
。原因之一是因为 IO
有默认错误表示为 Throwable
。不能只是混合 FailureMsg
和 Throwable
。要么需要继承以使 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
但是,flatMapF
的内容通常是一个单独的函数或效果,其中包括错误处理。像这样:
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
假设我有 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
的别名。我该怎么办?
我写了帮助程序 class 来做这件事。
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))
}
让我知道是否有更简洁的方法。
我会遵循类型。
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
如果 IO.raiseError
是这种情况,thenFlatMapF
不会产生 OptionT[IO, Int]
,因为 Throwable
没有默认映射到什么? IO.raiseError
.
第一次尝试修复它,将说明它:
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
和 return Option
的情况下处理错误,以便生成 OptionT[IO, Int]
?
所以基本上,在这种情况下,如果您希望 flatMapF
失败并需要有关错误的信息,那么最好将 EitherT
作为其容器,而不是 OptionT
.
完成后,可能的解决方案表明,在某个点 leftMap
或应执行方差以将 Throwable
映射到 FailureMsg
。原因之一是因为 IO
有默认错误表示为 Throwable
。不能只是混合 FailureMsg
和 Throwable
。要么需要继承以使 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
但是,flatMapF
的内容通常是一个单独的函数或效果,其中包括错误处理。像这样:
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