EitherT 上的左平面图
Left flatMap on EitherT
假设我有 return Future[Either[_, _]
的函数,我想在出现故障时应用其中的一些函数,这意味着只将它们应用到左侧。简化的例子是:
def operation1: Future[Either[String, Int]] = Future.successful(Right(5))
def operation2: Future[Either[String, Int]] = Future.successful(Left("error"))
def operation2FallBackWork = Future.successful{
println("Doing some revert stuff")
Left("Error happened, but reverting was successful")
}
val res = for {
res1 <- EitherT.fromEither(operation1)
res2 <- EitherT.fromEither(operation2)//.leftFlatMap(operation2FallBackWork) -????
} yield res1 + res2
Await.result(res.toEither, 5 seconds)
如何实现?
最接近 leftFlatMap
的是 MonadError
的 handleError
,它具有您期望的 leftFlatMap
签名(尽管请注意您需要将回退操作更改为 EitherT
并提供常量函数而不是传递它 as-is)。您可以像这样直接使用 EitherT
实例:
import scala.concurrent.{ Await, Future }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scalaz._, Scalaz._
def operation1: Future[Either[String, Int]] = Future.successful(Right(5))
def operation2: Future[Either[String, Int]] = Future.successful(Left("error"))
def operation2FallBack: EitherT[Future, String, Int] = EitherT(
Future.successful {
println("Doing some revert stuff")
"Error happened, but reverting was successful".left
}
)
val E: MonadError[({ type L[x] = EitherT[Future, String, x] })#L, String] =
implicitly
val res = for {
a <- EitherT.fromEither(operation1)
b <- E.handleError(EitherT.fromEither(operation2))(_ => operation2FallBack)
} yield a + b
Await.result(res.toEither, 5.seconds)
您还可以使用 MonadError
提供的语法使它看起来像 EitherT
有一个 handleError
方法,尽管需要更多的仪式才能让 Scala 编译器认识到您的操作具有正确的形状:
import scala.concurrent.{ Await, Future }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scalaz._, Scalaz._
type FE[x] = EitherT[Future, String, x]
def operation1: FE[Int] = EitherT(Future.successful(5.right))
def operation2: FE[Int] = EitherT(Future.successful("error".left))
def operation2FallBack: FE[Int] = EitherT(
Future.successful {
println("Doing some revert stuff")
"Error happened, but reverting was successful".left
}
)
val res = for {
a <- operation1
b <- operation2.handleError(_ => operation2FallBack)
} yield a + b
Await.result(res.toEither, 5.seconds)
我更喜欢第二个版本,但这是风格和品味的问题。
假设我有 return Future[Either[_, _]
的函数,我想在出现故障时应用其中的一些函数,这意味着只将它们应用到左侧。简化的例子是:
def operation1: Future[Either[String, Int]] = Future.successful(Right(5))
def operation2: Future[Either[String, Int]] = Future.successful(Left("error"))
def operation2FallBackWork = Future.successful{
println("Doing some revert stuff")
Left("Error happened, but reverting was successful")
}
val res = for {
res1 <- EitherT.fromEither(operation1)
res2 <- EitherT.fromEither(operation2)//.leftFlatMap(operation2FallBackWork) -????
} yield res1 + res2
Await.result(res.toEither, 5 seconds)
如何实现?
最接近 leftFlatMap
的是 MonadError
的 handleError
,它具有您期望的 leftFlatMap
签名(尽管请注意您需要将回退操作更改为 EitherT
并提供常量函数而不是传递它 as-is)。您可以像这样直接使用 EitherT
实例:
import scala.concurrent.{ Await, Future }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scalaz._, Scalaz._
def operation1: Future[Either[String, Int]] = Future.successful(Right(5))
def operation2: Future[Either[String, Int]] = Future.successful(Left("error"))
def operation2FallBack: EitherT[Future, String, Int] = EitherT(
Future.successful {
println("Doing some revert stuff")
"Error happened, but reverting was successful".left
}
)
val E: MonadError[({ type L[x] = EitherT[Future, String, x] })#L, String] =
implicitly
val res = for {
a <- EitherT.fromEither(operation1)
b <- E.handleError(EitherT.fromEither(operation2))(_ => operation2FallBack)
} yield a + b
Await.result(res.toEither, 5.seconds)
您还可以使用 MonadError
提供的语法使它看起来像 EitherT
有一个 handleError
方法,尽管需要更多的仪式才能让 Scala 编译器认识到您的操作具有正确的形状:
import scala.concurrent.{ Await, Future }
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scalaz._, Scalaz._
type FE[x] = EitherT[Future, String, x]
def operation1: FE[Int] = EitherT(Future.successful(5.right))
def operation2: FE[Int] = EitherT(Future.successful("error".left))
def operation2FallBack: FE[Int] = EitherT(
Future.successful {
println("Doing some revert stuff")
"Error happened, but reverting was successful".left
}
)
val res = for {
a <- operation1
b <- operation2.handleError(_ => operation2FallBack)
} yield a + b
Await.result(res.toEither, 5.seconds)
我更喜欢第二个版本,但这是风格和品味的问题。