【问题标题】:How to transform a Seq[Try] to a Try[Seq]如何将 Seq[Try] 转换为 Try[Seq]
【发布时间】:2019-09-25 00:59:18
【问题描述】:

使用Futures 可以轻松地将Seq[Future] 转换为Future[Seq]

Future.sequence(seqOfFutures)

我找不到与 Try 类似的东西。

它适用于foldLeft,但我真正喜欢的是Try.sequence(seqOfTry)

没有提供这样的功能有什么原因吗?

这是如何正确完成的?

语义:

成功的值列表:Success(Seq(1,2,3,4))

失败有两种可能:

  • 第一次失败Failure 并返回它。这是由这个问题处理的:listtryt-to-trylistt-in-scala

  • 收集所有Failures 并返回“复合”失败。

'compound'失败还有解决办法吗?

【问题讨论】:

  • 我会质疑因为一个 Failure 而丢弃每个 Success 的一般效用。
  • 这样一个函数的语义是什么?
  • @YuvalItzchakov 我在我的问题中添加了语义 - 我希望这就是你的意思。
  • @jwvh 同样的推理应该适用于Future

标签: scala


【解决方案1】:

根据 Luis 的建议,Validated 是为错误累积而设计的,因此请考虑 traverse 像这样

la.traverse(_.toEither.toValidatedNec)
lb.traverse(_.toEither.toValidatedNec)

哪个输出

res2: cats.data.ValidatedNec[Throwable,List[Int]] = Invalid(Chain(java.lang.RuntimeException: boom, java.lang.RuntimeException: crash))
res3: cats.data.ValidatedNec[Throwable,List[Int]] = Valid(List(1, 2, 3))

在哪里

import cats.syntax.traverse._
import cats.instances.list._
import cats.syntax.either._
import scala.util.{Failure, Success, Try}

val la: List[Try[Int]] = List(Success(1), Success(2), Failure(new RuntimeException("boom")), Success(3), Failure(new RuntimeException("crash")))
val lb: List[Try[Int]] = List(Success(1), Success(2), Success(3))

没有错误累积,我们可以像这样排序

import cats.implicits._
la.sequence 

哪个输出

res0: scala.util.Try[List[Int]] = Failure(java.lang.RuntimeException: boom)

【讨论】:

  • 我只想补充一点,Seq(如问题中所用)没有Traverse 实例。如果List 没问题,那么这就是要走的路。
  • 猫中是否也有一个版本会创建复合错误?
  • @pme 你能把 Trys 变成 Eithers 并复合所有的 Lefts 吗?
  • @pme 是的,但是您需要将您的 Try 更改为 Validated,或更改为 Either 并使用它的Parallel 实例 (反过来将使用 Validated。 - 此外,您的错误类型应该形成一个半组,以便能够组合错误。
  • @MarioGalic 恕我直言,最好x.toEither.toValidatedNecbimap 有点过于冗长,一般错误应该是 NonEmpty 以防无效,最后 Lists 的串联很糟糕。因此NonEmptyChain(对我而言)最好使用的类型。
【解决方案2】:

这是第二个问题的解答。

case class CompoundError(errs: List[Throwable]) extends Throwable

def toTry[T](list: List[Try[T]]): Try[List[T]] =
  list.partition(_.isSuccess) match {
    case (res, Nil) =>
      Success(res.map(_.get))
    case (_, errs) =>
      Failure(CompoundError(errs.collect { case Failure(e) => e }))
  }

partition操作区分成功和失败,match根据有无失败返回合适的值。


以前的解决方案:

case class CompoundError(errs: List[Throwable]) extends Throwable

def toTry[T](list: List[Try[T]]): Try[List[T]] = {
  val (res, errs) = list.foldLeft((List.empty[T], List.empty[Throwable])) {
    case ((res, errs), item) =>
      item match {
        case Success(t) => (t :: res, errs)
        case Failure(e) => (res, e :: errs)
      }
  }

  errs match {
    case Nil => Success(res.reverse)
    case _ => Failure(CompoundError(errs.reverse))
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    • 1970-01-01
    • 1970-01-01
    • 2014-08-25
    • 2019-07-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多