] = Validation.failureNel[A, X](self) @deprecated("use `failureNel` instead", "7.1") def failNel[X]: ValidationNel[A, X] = failureNel[X] } trait ToValidationOps { implicit def ToValidationOps[A](a: A) = new ValidationOps(a) }
上面的例子也可以这样写:
1 for { 2 a <- 3.success 3 b <- 2.success 4 } yield a + b //> res7: scalaz.Validation[Nothing,Int] = Success(5)
5
6 val pv = for { 7 a <- 3.success 8 c <- "oh, error!".failure[String] 9 d <- "oh, error again!".failure[String] 10 b <- 2.success 11 } yield a + b //> pv : scalaz.Validation[String,Int] = Failure(oh, error!)
12 if (pv.isFailure) (~pv).getOrElse("no error") //> res8: Any = oh, error!
不过上面两条异常信息只返回了头一条,这与\/并没有什么两样,因为它们的flatMap都是一样的:
final class ValidationFlatMap[E, A] private[scalaz](val self: Validation[E, A]) { /** Bind through the success of this validation. */ def flatMap[EE >: E, B](f: A => Validation[EE, B]): Validation[EE, B] = self match { case Success(a) => f(a) case e @ Failure(_) => e } }
当前版本的scalaz已经放弃了flatMap用法:
@deprecated("""flatMap does not accumulate errors, use `scalaz.\/` or `import scalaz.Validation.FlatMap._` instead""", "7.1") @inline implicit def ValidationFlatMapDeprecated[E, A](d: Validation[E, A]): ValidationFlatMap[E, A] =
new ValidationFlatMap(d) /** Import this if you wish to use `flatMap` without a deprecation * warning. */
object FlatMap { @inline implicit def ValidationFlatMapRequested[E, A](d: Validation[E, A]): ValidationFlatMap[E, A] =
new ValidationFlatMap(d) }
因为Validation又是个Applicative。它实现了ap函数:
/** Apply a function in the environment of the success of this validation, accumulating errors. */ def ap[EE >: E, B](x: => Validation[EE, A => B])(implicit E: Semigroup[EE]): Validation[EE, B] = (this, x) match { case (Success(a), Success(f)) => Success(f(a)) case (e @ Failure(_), Success(_)) => e case (Success(_), e @ Failure(_)) => e case (Failure(e1), Failure(e2)) => Failure(E.append(e2, e1)) }
我们可以同时运算几个Validation算法并返回所有异常信息:
((3.success : Validation[String,Int]) |@| ("oh, error1! ".failure : Validation[String,Int]) |@| (2.success : Validation[String,Int]) |@| ("oh, error2 again!".failure : Validation[String,Int])){_ + _ + _ + _} //> res13: scalaz.Validation[String,Int] = Failure(oh, error1! oh, error2 again!)
我们看到即使其中两项运算出现异常但还是完成了所有运算并且返回了两条异常信息。不过这两条信息合并在了String里,可能不方便后续处理。Validation注入方法提供了failureNel函数。我们试着用用:
((3.successNel : ValidationNel[String,Int]) |@| ("oh, error1! ".failureNel : ValidationNel[String,Int]) |@| (2.successNel : ValidationNel[String,Int]) |@| ("oh, error2 again!".failureNel : ValidationNel[String,Int])){_ + _ + _ + _} //> res14: scalaz.Validation[scalaz.NonEmptyList[String],Int] = Failure(NonEmptyList(oh, error1! , oh, error2 again!))
现在这两条信息被放进了NonEmptyList里。NonEmptyList就是一种List,不过没有Nil状态。看看它的定义:scalaz/NonEmptyList.scala
/** A singly-linked list that is guaranteed to be non-empty. */ final class NonEmptyList[+A] private[scalaz](val head: A, val tail: List[A]) { ...
至少这个List含有head元素。NonEmptyList的构建器在注入方法中:scalaz/NonEmptyListOps.scala
final class NelOps[A](self: A) { final def wrapNel: NonEmptyList[A] = NonEmptyList(self) } trait ToNelOps { implicit def ToNe