scalaz还提供了个type class叫Validation。乍看起来跟\/没什么分别。实际上这个Validation是在\/的基础上增加了Applicative功能,就是实现了ap函数。通过Applicative实例就可以同时运算多个Validation并返回多条异常信息。所以,\/与Validation核心分别就在于Validation可以返回多条异常信息。Validation也是由两种状态组成:Success和Failure,分别与\/的left和right相对应。Failure可以返回多个值。我们先来看看Validation在scalaz里的定义:scalaz/Validation.scala

  1. sealed abstract class Validation[+E, +A] extends Product with Serializable {
  2. ...
  3. def isSuccess: Boolean = this match {
  4. case Success(_) => true
  5. case Failure(_) => false
  6. }
  7.  
  8. /** Return `true` if this validation is failure. */
  9. def isFailure: Boolean = !isSuccess
  10. ...
  11. /** Return the success value of this validation or the given default if failure. Alias for `|` */
  12. def getOrElse[AA >: A](x: => AA): AA =
  13. this match {
  14. case Failure(_) => x
  15. case Success(a) => a
  16. }
  17.  
  18. /** Return the success value of this validation or the given default if failure. Alias for `getOrElse` */
  19. def |[AA >: A](x: => AA): AA =
  20. getOrElse(x)
  21.  
  22. /** Return the success value of this validation or run the given function on the failure. */
  23. def valueOr[AA >: A](x: E => AA): AA =
  24. this match {
  25. case Failure(a) => x(a)
  26. case Success(b) => b
  27. }
  28.  
  29. /** Return this if it is a success, otherwise, return the given value. Alias for `|||` */
  30. def orElse[EE >: E, AA >: A](x: => Validation[EE, AA]): Validation[EE, AA] =
  31. this match {
  32. case Failure(_) => x
  33. case Success(_) => this
  34. }
  35.  
  36. /** Return this if it is a success, otherwise, return the given value. Alias for `orElse` */
  37. def |||[EE >: E, AA >: A](x: => Validation[EE, AA]): Validation[EE, AA] =
  38. orElse(x)
  39. ...

与\/非常相似,也是提供了getOrElse来获取Success[A]的A值。如果需要获取Failure[B]值则与\/一样先用swap再用getOrElse:

  1. /** Flip the failure/success values in this validation. Alias for `unary_~` */
  2. def swap: Validation[A, E] =
  3. this match {
  4. case Failure(a) => Success(a)
  5. case Success(b) => Failure(b)
  6. }
  7.  
  8. Success().getOrElse() //> res5: Int = 3
  9. Success("Three").getOrElse("Everything OK!") //> res6: String = Three
  10. Failure("Something wrong!").swap.getOrElse("Everything OK!")
  11. //> res7: String = Something wrong!
  12. (~Failure("Something wrong!")).getOrElse("Everything OK!")
  13. //> res8: String = Something wrong!

Validation的两个状态是这样定义的:

  1. final case class Success[A](a: A) extends Validation[Nothing, A]
  2. final case class Failure[E](e: E) extends Validation[E, Nothing]

Validation也是一个Monad,可以在for-comprehension中实现Failure立即退出功能:

  1. for {
  2. a <- Success()
  3. b <- Success()
  4. } yield a + b //> res5: scalaz.Validation[Nothing,Int] = Success(5)
  5.  
  6. val valid= for {
  7. a <- Success()
  8. c <- Failure("oh, error!"): Validation[String,Int]
  9. d <- Failure("oh, error again!"): Validation[String,Int]
  10. b <- Success()
  11. } yield a + b //> valid : scalaz.Validation[String,Int] = Failure(oh, error!)
  12. if (valid.isFailure) valid.swap.getOrElse("no error")
  13. //> res6: Any = oh, error!

scalaz同样为所有类型值提供了注入方法:scalaz.syntax/ValidationOps.scala

  1. final class ValidationOps[A](self: A) {
  2. def success[X]: Validation[X, A] = Validation.success[X, A](self)
  3.  
  4. def successNel[X]: ValidationNel[X, A] = success
  5.  
  6. def failure[X]: Validation[A, X] = Validation.failure[A, X](self)
  7.  
  8. @deprecated("use `failure` instead", "7.1")
  9. def fail[X]: Validation[A, X] = failure[X]
  10.  
  11. def failureNel[X]: ValidationNel[A, X] = Validation.failureNel[A, X](self)
  12.  
  13. @deprecated("use `failureNel` instead", "7.1")
  14. def failNel[X]: ValidationNel[A, X] = failureNel[X]
  15. }
  16.  
  17. trait ToValidationOps {
  18. implicit def ToValidationOps[A](a: A) = new ValidationOps(a)
  19. }

上面的例子也可以这样写:

  1. for {
  2. a <- .success
  3. b <- .success
  4. } yield a + b //> res7: scalaz.Validation[Nothing,Int] = Success(5)
  5.  
  6. val pv = for {
  7. a <- .success
  8. c <- "oh, error!".failure[String]
  9. d <- "oh, error again!".failure[String]
  10. b <- .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都是一样的:

  1. final class ValidationFlatMap[E, A] private[scalaz](val self: Validation[E, A]) {
  2. /** Bind through the success of this validation. */
  3. def flatMap[EE >: E, B](f: A => Validation[EE, B]): Validation[EE, B] =
  4. self match {
  5. case Success(a) => f(a)
  6. case e @ Failure(_) => e
  7. }
  8. }

当前版本的scalaz已经放弃了flatMap用法:

  1. @deprecated("""flatMap does not accumulate errors, use `scalaz.\/` or `import scalaz.Validation.FlatMap._` instead""", "7.1")
  2. @inline implicit def ValidationFlatMapDeprecated[E, A](d: Validation[E, A]): ValidationFlatMap[E, A] =
  3. new ValidationFlatMap(d)
  4.  
  5. /** Import this if you wish to use `flatMap` without a deprecation
  6. * warning.
  7. */
  8. object FlatMap {
  9. @inline implicit def ValidationFlatMapRequested[E, A](d: Validation[E, A]): ValidationFlatMap[E, A] =
  10. new ValidationFlatMap(d)
  11. }

因为Validation又是个Applicative。它实现了ap函数:

  1. /** Apply a function in the environment of the success of this validation, accumulating errors. */
  2. def ap[EE >: E, B](x: => Validation[EE, A => B])(implicit E: Semigroup[EE]): Validation[EE, B] = (this, x) match {
  3. case (Success(a), Success(f)) => Success(f(a))
  4. case (e @ Failure(_), Success(_)) => e
  5. case (Success(_), e @ Failure(_)) => e
  6. case (Failure(e1), Failure(e2)) => Failure(E.append(e2, e1))
  7. }

我们可以同时运算几个Validation算法并返回所有异常信息:

  1. ((.success : Validation[String,Int]) |@|
  2. ("oh, error1! ".failure : Validation[String,Int]) |@|
  3. (.success : Validation[String,Int]) |@|
  4. ("oh, error2 again!".failure : Validation[String,Int])){_ + _ + _ + _}
  5. //> res13: scalaz.Validation[String,Int] = Failure(oh, error1! oh, error2 again!)

我们看到即使其中两项运算出现异常但还是完成了所有运算并且返回了两条异常信息。不过这两条信息合并在了String里,可能不方便后续处理。Validation注入方法提供了failureNel函数。我们试着用用:

  1. ((.successNel : ValidationNel[String,Int]) |@|
  2. ("oh, error1! ".failureNel : ValidationNel[String,Int]) |@|
  3. (.successNel : ValidationNel[String,Int]) |@|
  4. ("oh, error2 again!".failureNel : ValidationNel[String,Int])){_ + _ + _ + _}
  5. //> res14: scalaz.Validation[scalaz.NonEmptyList[String],Int] = Failure(NonEmptyList(oh, error1! , oh, error2 again!))

现在这两条信息被放进了NonEmptyList里。NonEmptyList就是一种List,不过没有Nil状态。看看它的定义:scalaz/NonEmptyList.scala

  1. /** A singly-linked list that is guaranteed to be non-empty. */
  2. final class NonEmptyList[+A] private[scalaz](val head: A, val tail: List[A]) {
  3. ...

至少这个List含有head元素。NonEmptyList的构建器在注入方法中:scalaz/NonEmptyListOps.scala

  1. final class NelOps[A](self: A) {
  2. final def wrapNel: NonEmptyList[A] =
  3. NonEmptyList(self)
  4. }
  5.  
  6. trait ToNelOps {
  7. implicit def ToNelOps[A](a: A) = new NelOps(a)
  8. }

我们简单地试用这个NonEmptyList:

  1. val nel = <:: <:: .wrapNel //> nel : scalaz.NonEmptyList[Int] = NonEmptyList(2, 4, 3)
  2. val snel = "one" <:: "two" <:: "three".wrapNel //> snel : scalaz.NonEmptyList[String] = NonEmptyList(one, two, three)
  3. nel.list //> res17: List[Int] = List(2, 4, 3)
  4. snel.list //> res18: List[String] = List(one, two, three)

我们可以直接把它转成List再进行处理操作。

Scalaz(20)-Monad: Validation-Applicative版本的Either的更多相关文章

  1. 泛函编程(25)-泛函数据类型-Monad-Applicative

    上两期我们讨论了Monad.我们说Monad是个最有概括性(抽象性)的泛函数据类型,它可以覆盖绝大多数数据类型.任何数据类型只要能实现flatMap+unit这组Monad最基本组件函数就可以变成Mo ...

  2. 泛函编程(26)-泛函数据类型-Monad-Applicative Functor Traversal

    前面我们讨论了Applicative.Applicative 就是某种Functor,因为我们可以用map2来实现map,所以Applicative可以map,就是Functor,叫做Applicat ...

  3. Monad / Functor / Applicative 浅析

    前言 Swift 其实比 Objective-C 复杂很多,相对于出生于上世纪 80 年代的 Objective-C 来说,Swift 融入了大量新特性.这也使得我们学习掌握这门语言变得相对来说更加困 ...

  4. Scalaz(41)- Free :IO Monad-Free特定版本的FP语法

    我们不断地重申FP强调代码无副作用,这样才能实现编程纯代码.像通过键盘显示器进行交流.读写文件.数据库等这些IO操作都会产生副作用.那么我们是不是为了实现纯代码而放弃IO操作呢?没有IO的程序就是一段 ...

  5. Scalaz(19)- Monad: \/ - Monad 版本的 Either

    scala标准库提供了一个Either类型,它可以说是Option的升级版.与Option相同,Either也有两种状态:Left和Right,分别对应Option的None和Some,不同的是Lef ...

  6. Scalaz(10)- Monad:就是一种函数式编程模式-a design pattern

    Monad typeclass不是一种类型,而是一种程序设计模式(design pattern),是泛函编程中最重要的编程概念,因而很多行内人把FP又称为Monadic Programming.这其中 ...

  7. 浅释Functor、Applicative与Monad

    引言 转入Scala一段时间以来,理解Functor.Applicative和Monad等概念,一直是我感到头疼的部分.虽然读过<Functors, Applicatives, And Mona ...

  8. 泛函编程(27)-泛函编程模式-Monad Transformer

    经过了一段时间的学习,我们了解了一系列泛函数据类型.我们知道,在所有编程语言中,数据类型是支持软件编程的基础.同样,泛函数据类型Foldable,Monoid,Functor,Applicative, ...

  9. Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC 配置校验器

    Spring4新特性——泛型限定式依赖注入 Spring4新特性——核心容器的其他改进 Spring4新特性——Web开发的增强 Spring4新特性——集成Bean Validation 1.1(J ...

随机推荐

  1. Atitit  循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate).

    Atitit  循环(loop), 递归(recursion), 遍历(traversal), 迭代(iterate). 1.1. 循环算是最基础的概念, 凡是重复执行一段代码, 都可以称之为循环. ...

  2. JS操作页面

    DOM操作 1 windows对象操作 属性(值或者子对象): opener:打开当前窗口的源窗口,如果当前窗口是首次启动浏览器打开的,则opener是null.可以利用这属性来关闭源窗口 方法(函数 ...

  3. iOS-MVC模式

    提到ios中的mvc不得不提2011秋季斯坦福课程的老头,他的iphone开发公开课是所有描述ios中mvc模式最为准确并且最为浅显易懂的. 模型-视图-控制器 这个模式其实应该叫做MCV,用控制器把 ...

  4. SpringSide 部署showcase项目出现 JAX-RS (REST Web Services) 2.0 can not be installed错误!

    maven+springmvc错误 JAX-RS (REST Web Services) 2.0 can not be installed 项目problem提示错误 JAX-RS (REST Web ...

  5. lufylegend游戏引擎

    lufylegend游戏引擎介绍:click 这个链接我觉得已经很详细的介绍了这个引擎. 所以以下我只说说一些简单的游戏代码过程. 首先从canvas做游戏叙述起: 这是一个让人很熟悉的简单小游戏,网 ...

  6. LLBL Gen + Entity Framework 程序设计入门

    Entity Framework推出有好几年,除了微软的Visual Studio可以做实体框架开发外,第三方的开发工具如LLBL Gen, Devart Entity Developer也可以用来做 ...

  7. backbone库学习-model

    backbone库的结构: http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html 本文所有例子来自于http://blog.cs ...

  8. [OpenCV] Samples 11: image sequence

    一帧一帧地读取视频流. VideoCapture sequence(file_video); sequence >> image. #include <opencv2/core/co ...

  9. 10个优秀的 HTML5 & CSS3 下拉菜单制作教程

    下拉菜单是一个很常见的效果,在网站设计中被广泛使用.通过使用下拉菜单,设计者不仅可以在网站设计中营造出色的视觉吸引力,但也可以为网站提供了一个有效的导航方案.使用 HTML5 和 CSS3 可以更容易 ...

  10. 【Android】Android ObjectAnimator动画初识、模仿

    ObjectAnimator: ObjectAnimator的概念这里就不解释了,直接从代码中说明,以下是模仿Persicope的加载动画,简单的几行代码即可实现,当然我也是模仿的,更好的实现思路还请 ...