Link 多个二选一
Link Multiple Either's
考虑场景
trait Checker {
def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]]
}
此特性由各种 class 实现。
假设
class CheckerImpl1 extends Checker {
override def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]] = ???
}
class CheckerImpl2 extends Checker {
override def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]] = ???
}
现在,我需要定义一个新函数,它将为每个实现 class 依次调用 check()
函数(顺序无关紧要)和 return 一个新函数即 Future[Either[String, Unit]]
此处的字符串是 left
的连接字符串,用于 check()
实现结果。
所以如果 CheckerImpl1.check()
returns Left("error1")
和 CheckerImpl2.check()
returns Left("error2")
那么新函数的结果将 return Left("error1&error2")
(&只是分开两个字符串)。
或
所以如果 CheckerImpl1.check()
returns Right(())
和 CheckerImpl2.check()
returns Left("error2")
那么新函数的结果将 return Left("error2")
.
或
所以如果 CheckerImpl1.check()
returns Right(())
和 CheckerImpl2.check()
returns Right(())
那么新函数的结果将 return Right(())
.
我现在所做的是
(CheckerImpl1.check(), CheckerImpl2.check())
.mapN {
case (Right(_), Right(_)) => Right(())
case (Left(err), Right(_)) => Left(err)
case (Right(_), Left(err)) => Left(err)
case (Left(err1), Left(err2)) => Left(err1 ++ "&" ++ err2)))
}
但这不是一个理想的解决方案,因为如果我添加更多实现,那么我将需要添加更多这些 case 语句。
有更好的方法吗?
所以你有 List
个 Future
Either
秒。
val lfe :List[Future[Either[String,Unit]]] = ???
要将所有 Left
个字符串放在一个 Future[String]
中,您可以这样做...
val res :Future[String] =
Future.sequence(lfe)
.map(_.flatMap(_.fold(Some(_),_ => None)).mkString(" & "))
如果我没理解错的话,你最终想要得到的是Future[Either[String, Unit]]
类型。为什么不只是 .sequence
期货和 .fold
结果?
val checkers: List[Checker] = ???
Future.sequence(checkers.map(_.check()))
.map { results => results.foldLeft(Right(()): Either[String, Unit]) {
case (Left(acc), Left(err)) => Left(s"$acc&$err")
case (Right(_), Left(err)) => Left(err)
case (acc, Right(_)) => acc
}}
您现在唯一需要更改的代码是扩充 checkers
列表。
稍微 更多 使用 cats
优雅(如果您不熟悉 kind projector plugin - 这就是 *
的来源)。
import cats.implicilts._
checkers.map(_.check()).sequence
.map { results =>
results.map(_.toValidatedNec)
.sequence[ValidatedNec[String, *], Unit]
.leftMap(_.toList.mkString("&"))
.map(_ => ())
.toEither
}
考虑场景
trait Checker {
def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]]
}
此特性由各种 class 实现。
假设
class CheckerImpl1 extends Checker {
override def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]] = ???
}
class CheckerImpl2 extends Checker {
override def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]] = ???
}
现在,我需要定义一个新函数,它将为每个实现 class 依次调用 check()
函数(顺序无关紧要)和 return 一个新函数即 Future[Either[String, Unit]]
此处的字符串是 left
的连接字符串,用于 check()
实现结果。
所以如果 CheckerImpl1.check()
returns Left("error1")
和 CheckerImpl2.check()
returns Left("error2")
那么新函数的结果将 return Left("error1&error2")
(&只是分开两个字符串)。
或
所以如果 CheckerImpl1.check()
returns Right(())
和 CheckerImpl2.check()
returns Left("error2")
那么新函数的结果将 return Left("error2")
.
或
所以如果 CheckerImpl1.check()
returns Right(())
和 CheckerImpl2.check()
returns Right(())
那么新函数的结果将 return Right(())
.
我现在所做的是
(CheckerImpl1.check(), CheckerImpl2.check())
.mapN {
case (Right(_), Right(_)) => Right(())
case (Left(err), Right(_)) => Left(err)
case (Right(_), Left(err)) => Left(err)
case (Left(err1), Left(err2)) => Left(err1 ++ "&" ++ err2)))
}
但这不是一个理想的解决方案,因为如果我添加更多实现,那么我将需要添加更多这些 case 语句。
有更好的方法吗?
所以你有 List
个 Future
Either
秒。
val lfe :List[Future[Either[String,Unit]]] = ???
要将所有 Left
个字符串放在一个 Future[String]
中,您可以这样做...
val res :Future[String] =
Future.sequence(lfe)
.map(_.flatMap(_.fold(Some(_),_ => None)).mkString(" & "))
如果我没理解错的话,你最终想要得到的是Future[Either[String, Unit]]
类型。为什么不只是 .sequence
期货和 .fold
结果?
val checkers: List[Checker] = ???
Future.sequence(checkers.map(_.check()))
.map { results => results.foldLeft(Right(()): Either[String, Unit]) {
case (Left(acc), Left(err)) => Left(s"$acc&$err")
case (Right(_), Left(err)) => Left(err)
case (acc, Right(_)) => acc
}}
您现在唯一需要更改的代码是扩充 checkers
列表。
稍微 更多 使用 cats
优雅(如果您不熟悉 kind projector plugin - 这就是 *
的来源)。
import cats.implicilts._
checkers.map(_.check()).sequence
.map { results =>
results.map(_.toValidatedNec)
.sequence[ValidatedNec[String, *], Unit]
.leftMap(_.toList.mkString("&"))
.map(_ => ())
.toEither
}