【问题标题】:Link Multiple Either's链接多个任意一个
【发布时间】:2020-07-12 18:07:23
【问题描述】:

考虑场景

trait Checker {
  def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]]
} 

这个 trait 由各种类实现。

让我们说

    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]] = ???
        } 

现在,我需要定义一个新函数,它将按顺序为这些实现类中的每一个调用check() 函数(顺序无关紧要)并返回一个新的,即Future[Either[String, Unit]],这里的String 是@ 的串联字符串987654325@为check()实现结果。

所以如果CheckerImpl1.check() 返回Left("error1") 并且CheckerImpl2.check() 返回Left("error2"),那么新函数的结果将返回Left("error1&error2")(并且只是分隔两个字符串)。

所以如果CheckerImpl1.check() 返回Right(()) 并且CheckerImpl2.check() 返回Left("error2") 那么新函数的结果将返回Left("error2")

所以如果CheckerImpl1.check() 返回Right(()) 并且CheckerImpl2.check() 返回Right(()),那么新函数的结果将返回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 语句。

有没有更好的方法?

【问题讨论】:

    标签: scala either


    【解决方案1】:

    所以你有一个List Future Eithers。

    val lfe :List[Future[Either[String,Unit]]] = ???
    

    要将所有 Left 字符串放在一个 Future[String] 中,您可以这样做...

    val res :Future[String] =
      Future.sequence(lfe)
            .map(_.flatMap(_.fold(Some(_),_ => None)).mkString(" & "))
    

    【讨论】:

      【解决方案2】:

      如果我理解正确,你最终想要得到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
        }
      

      【讨论】:

        猜你喜欢
        • 2020-08-14
        • 2021-11-27
        • 1970-01-01
        • 1970-01-01
        • 2016-06-16
        • 2022-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多