Scalaz(20)-Monad: Validation-Applicative版本的Either
scalaz还提供了个type class叫Validation。乍看起来跟\/没什么分别。实际上这个Validation是在\/的基础上增加了Applicative功能,就是实现了ap函数。通过Applicative实例就可以同时运算多个Validation并返回多条异常信息。所以,\/与Validation核心分别就在于Validation可以返回多条异常信息。Validation也是由两种状态组成:Success和Failure,分别与\/的left和right相对应。Failure可以返回多个值。我们先来看看Validation在scalaz里的定义:scalaz/Validation.scala
sealed abstract class Validation[+E, +A] extends Product with Serializable {
...
def isSuccess: Boolean = this match {
case Success(_) => true
case Failure(_) => false
}
/** Return `true` if this validation is failure. */
def isFailure: Boolean = !isSuccess
...
/** Return the success value of this validation or the given default if failure. Alias for `|` */
def getOrElse[AA >: A](x: => AA): AA =
this match {
case Failure(_) => x
case Success(a) => a
}
/** Return the success value of this validation or the given default if failure. Alias for `getOrElse` */
def |[AA >: A](x: => AA): AA =
getOrElse(x)
/** Return the success value of this validation or run the given function on the failure. */
def valueOr[AA >: A](x: E => AA): AA =
this match {
case Failure(a) => x(a)
case Success(b) => b
}
/** Return this if it is a success, otherwise, return the given value. Alias for `|||` */
def orElse[EE >: E, AA >: A](x: => Validation[EE, AA]): Validation[EE, AA] =
this match {
case Failure(_) => x
case Success(_) => this
}
/** Return this if it is a success, otherwise, return the given value. Alias for `orElse` */
def |||[EE >: E, AA >: A](x: => Validation[EE, AA]): Validation[EE, AA] =
orElse(x)
...
与\/非常相似,也是提供了getOrElse来获取Success[A]的A值。如果需要获取Failure[B]值则与\/一样先用swap再用getOrElse:
/** Flip the failure/success values in this validation. Alias for `unary_~` */
def swap: Validation[A, E] =
this match {
case Failure(a) => Success(a)
case Success(b) => Failure(b)
} Success().getOrElse() //> res5: Int = 3
Success("Three").getOrElse("Everything OK!") //> res6: String = Three
Failure("Something wrong!").swap.getOrElse("Everything OK!")
//> res7: String = Something wrong!
(~Failure("Something wrong!")).getOrElse("Everything OK!")
//> res8: String = Something wrong!
Validation的两个状态是这样定义的:
final case class Success[A](a: A) extends Validation[Nothing, A]
final case class Failure[E](e: E) extends Validation[E, Nothing]
Validation也是一个Monad,可以在for-comprehension中实现Failure立即退出功能:
for {
a <- Success()
b <- Success()
} yield a + b //> res5: scalaz.Validation[Nothing,Int] = Success(5)
val valid= for {
a <- Success()
c <- Failure("oh, error!"): Validation[String,Int]
d <- Failure("oh, error again!"): Validation[String,Int]
b <- Success()
} yield a + b //> valid : scalaz.Validation[String,Int] = Failure(oh, error!)
if (valid.isFailure) valid.swap.getOrElse("no error")
//> res6: Any = oh, error!
scalaz同样为所有类型值提供了注入方法:scalaz.syntax/ValidationOps.scala
final class ValidationOps[A](self: A) {
def success[X]: Validation[X, A] = Validation.success[X, A](self)
def successNel[X]: ValidationNel[X, A] = success
def failure[X]: Validation[A, X] = Validation.failure[A, X](self)
@deprecated("use `failure` instead", "7.1")
def fail[X]: Validation[A, X] = failure[X]
def failureNel[X]: ValidationNel[A, X] = 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)
}
上面的例子也可以这样写:
for {
a <- .success
b <- .success
} yield a + b //> res7: scalaz.Validation[Nothing,Int] = Success(5)
val pv = for {
a <- .success
c <- "oh, error!".failure[String]
d <- "oh, error again!".failure[String]
b <- .success
} yield a + b //> pv : scalaz.Validation[String,Int] = Failure(oh, error!)
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算法并返回所有异常信息:
((.success : Validation[String,Int]) |@|
("oh, error1! ".failure : Validation[String,Int]) |@|
(.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函数。我们试着用用:
((.successNel : ValidationNel[String,Int]) |@|
("oh, error1! ".failureNel : ValidationNel[String,Int]) |@|
(.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 ToNelOps[A](a: A) = new NelOps(a)
}
我们简单地试用这个NonEmptyList:
val nel = <:: <:: .wrapNel //> nel : scalaz.NonEmptyList[Int] = NonEmptyList(2, 4, 3)
val snel = "one" <:: "two" <:: "three".wrapNel //> snel : scalaz.NonEmptyList[String] = NonEmptyList(one, two, three)
nel.list //> res17: List[Int] = List(2, 4, 3)
snel.list //> res18: List[String] = List(one, two, three)
我们可以直接把它转成List再进行处理操作。
Scalaz(20)-Monad: Validation-Applicative版本的Either的更多相关文章
- 泛函编程(25)-泛函数据类型-Monad-Applicative
上两期我们讨论了Monad.我们说Monad是个最有概括性(抽象性)的泛函数据类型,它可以覆盖绝大多数数据类型.任何数据类型只要能实现flatMap+unit这组Monad最基本组件函数就可以变成Mo ...
- 泛函编程(26)-泛函数据类型-Monad-Applicative Functor Traversal
前面我们讨论了Applicative.Applicative 就是某种Functor,因为我们可以用map2来实现map,所以Applicative可以map,就是Functor,叫做Applicat ...
- Monad / Functor / Applicative 浅析
前言 Swift 其实比 Objective-C 复杂很多,相对于出生于上世纪 80 年代的 Objective-C 来说,Swift 融入了大量新特性.这也使得我们学习掌握这门语言变得相对来说更加困 ...
- Scalaz(41)- Free :IO Monad-Free特定版本的FP语法
我们不断地重申FP强调代码无副作用,这样才能实现编程纯代码.像通过键盘显示器进行交流.读写文件.数据库等这些IO操作都会产生副作用.那么我们是不是为了实现纯代码而放弃IO操作呢?没有IO的程序就是一段 ...
- Scalaz(19)- Monad: \/ - Monad 版本的 Either
scala标准库提供了一个Either类型,它可以说是Option的升级版.与Option相同,Either也有两种状态:Left和Right,分别对应Option的None和Some,不同的是Lef ...
- Scalaz(10)- Monad:就是一种函数式编程模式-a design pattern
Monad typeclass不是一种类型,而是一种程序设计模式(design pattern),是泛函编程中最重要的编程概念,因而很多行内人把FP又称为Monadic Programming.这其中 ...
- 浅释Functor、Applicative与Monad
引言 转入Scala一段时间以来,理解Functor.Applicative和Monad等概念,一直是我感到头疼的部分.虽然读过<Functors, Applicatives, And Mona ...
- 泛函编程(27)-泛函编程模式-Monad Transformer
经过了一段时间的学习,我们了解了一系列泛函数据类型.我们知道,在所有编程语言中,数据类型是支持软件编程的基础.同样,泛函数据类型Foldable,Monoid,Functor,Applicative, ...
- Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC 配置校验器
Spring4新特性——泛型限定式依赖注入 Spring4新特性——核心容器的其他改进 Spring4新特性——Web开发的增强 Spring4新特性——集成Bean Validation 1.1(J ...
随机推荐
- Mybatis入门例子
Mybatis是轻量级的持久化框架,的确上手非常快. Mybatis大体上的思路就是由一个总的config文件配置全局的信息,比如mysql连接信息等.然后再mapper中指定查询的sql,以及参数和 ...
- 程序员藏经阁 Linux兵书
程序员藏经阁 Linux兵书 刘丽霞 杨宇 编 ISBN 978-7-121-21992-4 2014年1月出版 定价:79.00元 536页 16开 内容提要 <Linux兵书>由浅 ...
- java的五种数据类型解析
不知道大家对java的简单数据类型是否了解,下面针对Java的五种类型简单数据类型表示数字和字符,进行详细的讲解和分析. 一.简单数据类型初始化 在Java语言中,简单数据类型作为类的成员变量声明时自 ...
- salesforce 零基础开发入门学习(七)PickList的value值获取
之前介绍过PickList类型的声明以及赋值,但是如何取出呢?一个sObject对象可以理解为一条数据.通过sObject直接取恐怕很难做到,因为他只会显示一个值.这时候就要用到Schema命名空间中 ...
- 让AngularJS的$http 服务像jQuery.ajax()一样工作
让AngularJS的$http 服务像jQuery.ajax()一样工作 $http的post . 请求默认的content-Type=application/json . 提交的是json对象的字 ...
- 快速入门系列--MVC--02路由
现在补上URL路由的学习,至于蒋老师自建的MVC小引擎和相关案例就放在论文提交后再实践咯.通过ASP.NET的路由系统,可以完成请求URL与物理文件的分离,其优点是:灵活性.可读性.SEO优化.接下来 ...
- 编译原理 LL1文法First集算法实现
import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap ...
- CSS3的学习--实现瀑布流
基于CSS3实现瀑布流,使用CSS3的CSS 多栏(Multi-column). 可以到github上下载源码 : https://github.com/CraryPrimitiveMan/water ...
- Windows Azure Virtual Machine (26) 使用高级存储(SSD)和DS系列VM
<Windows Azure Platform 系列文章目录> Update: 2016-11-3,如果大家在使用Linux VM,使用FIO进行IOPS测试的时候,请使用以下命令: su ...
- SQL Server安全(1/11):SQL Server安全概述
在保密你的服务器和数据,防备当前复杂的攻击,SQL Server有你需要的一切.但在你能有效使用这些安全功能前,你需要理解你面对的威胁和一些基本的安全概念.这篇文章提供了基础,因此你可以对SQL Se ...