Scala: объединение любого в весь список с любым из элементов - PullRequest
3 голосов
/ 22 мая 2019

У меня есть список 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, я хочу объединить все ошибки всех и вернуть их.

Я нашел похожую тему в [ Как уменьшить Seq [Either [A, B]] до 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 ]

4 голосов
/ 22 мая 2019

Как уже упоминалось в комментариях, Either хорошо для безотказного поведения. Для накопления нескольких ошибок вы, вероятно, захотите что-то вроде Validated. Кроме того:

  • Список можно просмотреть (есть экземпляр Traverse)
  • Утверждено аппликативно
  • Validated.fromEither отображает Either[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
3 голосов
/ 22 мая 2019

Как @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 больше подходит для fail fast ошибок.

...