Scala:将整个列表中的每个元素与每个元素中的每个元素组合

时间:2019-05-22 13:50:41

标签: scala validation error-handling scala-cats either

我有一个Either列表,表示错误:

type ErrorType = List[String]
type FailFast[A] = Either[ErrorType, A]

import cats.syntax.either._
val l = List(1.asRight[ErrorType], 5.asRight[ErrorType])

如果所有人都正确,我想获得[A]的列表,在这种情况下-List[Int]

如果还有任何Either,我想合并所有两个的所有错误并将其返回。

我在[How to reduce a Seq[Either[A,B]] to a Either[A,Seq[B]]

找到了类似的主题

但是很久以前。例如,答案之一提供了使用partitionMap的功能,目前我找不到。可能有一个更好,更优雅的解决方案。斯卡拉猫的例子很好。

我想如何使用它:

for {
  listWithEihers <- someFunction
  //if this list contains one or more errors, return Left[List[String]]
  //if everything is fine, convert it to:
  correctItems <- //returns list of List[Int] as right
} yield correctItems

此理解的返回类型必须为:

Either[List[String], List[Int]]

2 个答案:

答案 0 :(得分:4)

正如评论中已经提到的,Either对于快速失败行为很有用。为了累积多个错误,您可能需要类似Validated的东西。而且:

  • 列表是可遍历的(具有Traverse的实例)
  • 已验证是适用的
  • Validated.fromEitherEither[List[String], X]映射到Validated[List[String], X],这正是您在traverse中的功能所需要的。

因此,您可以尝试:

  • l.traverse(Validated.fromEither),如果您可以接受Validated
  • l.traverse(Validated.fromEither).toEither,如果您真的想要Either

所有导入的完整示例:

import cats.data.Validated
import cats.syntax.validated._
import cats.syntax.either._
import cats.syntax.traverse._
import cats.instances.list._
import cats.Traverse
import scala.util.Either

type ErrorType = List[String]
type FailFast[A] = Either[ErrorType, A]
val l: List[Either[ErrorType, Int]] = List(1.asRight[ErrorType], 5.asRight[ErrorType])

// solution if you want to keep a `Validated`
val validatedList: Validated[ErrorType, List[Int]] =
  l.traverse(Validated.fromEither)

// solution if you want to transform it back to `Either`
val eitherList: Either[ErrorType, List[Int]] =    
  l.traverse(Validated.fromEither).toEither

答案 1 :(得分:3)

正如@Luis在评论中提到的,ValidatedNel是您要寻找的:

import cats.data.{ Validated, ValidatedNel }
import cats.implicits._

type ErrorType = String

def combine(listWithEither: List[Either[ErrorType, Int]]):ValidatedNel[ErrorType, List[Int]] =
      listWithEither.foldMap(e => Validated.fromEither(e).map(List(_)).toValidatedNel)

      val l1 = List[Either[ErrorType, Int]](Right(1), Right(2), Right(3))
      val l2 = List[Either[ErrorType, Int]](Left("Incorrect String"), Right(2), Left("Validation error"))

println(combine(l1))
// Displays Valid(List(1, 2, 3))

println(combine(l2))
// Displays Invalid(NonEmptyList(Incorrect String, Validation error))

您可以使用Either将最终文本转换回.toEither,但是ValidatedNel是累积错误的更好结构,而Either更适合{{ 1}}错误。