从聚合的 ValidationNel 访问成功和失败

Access both the success and the failure from an aggregated ValidationNel

是否可以使用 ValidationNel 来 return 错误和成功的混合?

举个例子:

def f(i: Int) =
  if (i > 2) i.successNel
  else "something wrong".failureNel

List(1, 2, 3).traverseU(f) // Failure(NonEmptyList(something wrong, something wrong))

借用 How to use Scalaz's traverse and traverseU with Either 我们得到了两次失败 (1,2) 和一次成功 (3)。由于我们有超过 0 次失败,结果是失败,但是有什么方法可以从 3 中获得成功,还是只是被丢弃了?

编辑:看 for separate from MonadPlus.


TryEither 或 scalaz 的析取 \/ 相比,您使用 Validation[E, X] 并因此也使用 ValidationNel[E, X] 来累积错误给你第一个失败。

traverse函数是mapsequence的组合。

List(1, 2, 3).map(f).sequenceU == List(1, 2, 3).traverseU(f)

如果我们查看映射后的中间结果:

List(1, 2, 3).map(f)
// List[scalaz.Validation[scalaz.NonEmptyList[String],Int]] = 
// List(Failure(NonEmptyList(something wrong)), 
//   Failure(NonEmptyList(something wrong)), 
//   Success(3))

我们可以看到我们的中间结果是 Validation[NonEmptyList[String], Int]] 类型,等于 ValidationNel[String, Int].

如果我们再看最后一步(sequence) :

  • sequence 是一个将 monad 翻转过来的函数 (F[G[_]] => G[F[_]]),在这种情况下我们从 List[ValidationNel[String, Int]]ValidationNel[String, List[Int]].
  • Validation的上下文是我们想知道我们的函数是否成功,如果失败则累积所有错误。

因此,使用 sequence 我们会丢失有关列表中哪些元素成功的信息。

如果你真的想同时获得成功和失败,更简洁的方法是使用 .separate

import scalaz.syntax.validation._
import scalaz.syntax.monadPlus._
import scalaz.std.list._

@ val a = "yeah".success[Int]
a: Validation[Int, String] = Success(yeah)
@ val b = "wot".success[Int]
b: Validation[Int, String] = Success(wot)
@ val c = 10.failure[String]
c: Validation[Int, String] = Failure(10)
@ List(a,b,c).separate
res6: (List[Int], List[String]) = (List(10), List("yeah", "wot"))