scala标准库提供了一个Either类型,它可以说是Option的升级版。与Option相同,Either也有两种状态:Left和Right,分别对应Option的None和Some,不同的是Left可以返回一个值。我们通常用这个值来表述异常信息。scalaz也提供了自己版本的Either,并用\/来分辨表示,以及两种状态-\/和\/-。我想scalaz特别提供\/是有原因的:\/不单是一种类型,它是一种type class。更重要的是\/是一种Monad,具备了函数组合能力(composibility)。如此能够方便把Either功能整合到FP编程中去。我们先看看\/的定义:scalaz/Either.scala

sealed abstract class \/[+A, +B] extends Product with Serializable {
...
def isLeft: Boolean =
this match {
case -\/(_) => true
case \/-(_) => false
} /** Return `true` if this disjunction is right. */
def isRight: Boolean =
this match {
case -\/(_) => false
case \/-(_) => true
}
...
/** Return the right value of this disjunction or the given default if left. Alias for `|` */
def getOrElse[BB >: B](x: => BB): BB =
this match {
case -\/(_) => x
case \/-(b) => b
} /** Return the right value of this disjunction or the given default if left. Alias for `getOrElse` */
def |[BB >: B](x: => BB): BB =
getOrElse(x) /** Return the right value of this disjunction or run the given function on the left. */
def valueOr[BB >: B](x: A => BB): BB =
this match {
case -\/(a) => x(a)
case \/-(b) => b
} /** Return this if it is a right, otherwise, return the given value. Alias for `|||` */
def orElse[AA >: A, BB >: B](x: => AA \/ BB): AA \/ BB =
this match {
case -\/(_) => x
case \/-(_) => this
} /** Return this if it is a right, otherwise, return the given value. Alias for `orElse` */
def |||[AA >: A, BB >: B](x: => AA \/ BB): AA \/ BB =
orElse(x)
...

与Option相同:\/也提供了函数来获取运算值(Right[A]),如getOrElse。那么如何获取异常信息呢?可以用swap后再用getOrElse:

  /** Flip the left/right values in this disjunction. Alias for `unary_~` */
def swap: (B \/ A) =
this match {
case -\/(a) => \/-(a)
case \/-(b) => -\/(b)
} /** Flip the left/right values in this disjunction. Alias for `swap` */
def unary_~ : (B \/ A) =
swap "ah, error!".left[Int].getOrElse("no error") //> res2: Any = no error
"ah, error!".left[Int].swap.getOrElse("no error") //> res3: String = ah, error!
(~"ah, error!".left[Int]).getOrElse("no error") //> res4: String = ah, error!

与Option一样,\/也有两种状态:

/** A left disjunction
*
* Often used to represent the failure case of a result
*/
final case class -\/[+A](a: A) extends (A \/ Nothing) /** A right disjunction
*
* Often used to represent the success case of a result
*/
final case class \/-[+B](b: B) extends (Nothing \/ B)

\/实现了map和flatMap:

  /** Map on the right of this disjunction. */
def map[D](g: B => D): (A \/ D) =
this match {
case \/-(a) => \/-(g(a))
case b @ -\/(_) => b
}
/** Bind through the right of this disjunction. */
def flatMap[AA >: A, D](g: B => (AA \/ D)): (AA \/ D) =
this match {
case a @ -\/(_) => a
case \/-(b) => g(b)
}

注意flatMap:如果状态为\/- 则连续运算g(b),如果状态为-\/ 则立即停止运算返回-\/状态。这与Option功能相当。我们用for-comprehension来证明:

 val epok = for {
a <- \/-()
b <- \/-()
} yield a + b //> epok : scalaz.\/[Nothing,Int] = \/-(5)
val epno = for {
a <- \/-()
c <- -\/("breaking out...")
b <- \/-()
} yield a + b //> epno : scalaz.\/[String,Int] = -\/(breaking out...)
if (epno.isLeft) (~epno).getOrElse("no error") //> res5: Any = breaking out...

\/在for-comprehension里的运算行为与Option一致。不过这个\/写法比较别扭。\/type class为任何类型提供了注入方法left和right: scalaz.syntax/EitherOps.scala

  final def left[B]: (A \/ B) =
-\/(self) final def right[B]: (B \/ A) =
\/-(self)
} trait ToEitherOps { //可以为任何类型A注入方法
implicit def ToEitherOps[A](a: A) = new EitherOps(a)
}

现在这个for-comprehension可以这样写:

 val epok1 = for {
a <- .right
b <- .right
} yield a + b //> epok1 : scalaz.\/[Nothing,Int] = \/-(5)
val epno1 = for {
a <- .right
c <- "breaking out...".left[Int]
b <- .right
} yield a + b //> epno1 : scalaz.\/[String,Int] = -\/(breaking out...)
if (epno1.isLeft) (~epno1).getOrElse("no error") //> res6: Any = breaking out...

这样表述是不是清晰直白多了。

Scalaz(19)- Monad: \/ - Monad 版本的 Either的更多相关文章

  1. Ubuntu 系统安装详解 19.04最新版本

    Ubuntu 19.04版本系统安装详解 1 .镜像的下载 推荐 阿里云镜像下载 2.安装 1.1.新建虚拟机 注意硬件的兼容性问题 当前只有5.x可以用,其他兼容各位可以尝试下,我也都试过,但只有5 ...

  2. centos 7.4 安装docker 19.03.6 版本。附带离线安装包

    说明: 1.此环境为未安装过docker服务的环境, 如果已经安装,则自行卸载. 2.以下环境中上传的包及离线yum源默认为/home目录下,如无特殊说明,以此目录为准 步骤一:下载docker离线安 ...

  3. 2015第19周四jquery版本

    今天用到一个jquery插件,发现最新版需要jquery2.0以上版本才行,而目前项目在用的版本是1.8.3,自然无法使用,刚看了jquery的主要版本和差异,直接百度搜索无满意结果,最后在百科中给出 ...

  4. Azkaban3.X的安装(2018年8月19日最新版本)

    参考文章: 1.http://azkaban.github.io/azkaban/docs/latest/ 2.http://blog.csdn.net/gaoqida/article/details ...

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

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

  6. Scalaz(25)- Monad: Monad Transformer-叠加Monad效果

    中间插播了几篇scalaz数据类型,现在又要回到Monad专题.因为FP的特征就是Monad式编程(Monadic programming),所以必须充分理解认识Monad.熟练掌握Monad运用.曾 ...

  7. Scalaz(17)- Monad:泛函状态类型-State Monad

    我们经常提到函数式编程就是F[T].这个F可以被视为一种运算模式.我们是在F运算模式的壳子内对T进行计算.理论上来讲,函数式程序的运行状态也应该是在这个运算模式壳子内的,也是在F[]内更新的.那么我们 ...

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

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

  9. 泛函编程(23)-泛函数据类型-Monad

    简单来说:Monad就是泛函编程中最概括通用的数据模型(高阶数据类型).它不但涵盖了所有基础类型(primitive types)的泛函行为及操作,而且任何高阶类或者自定义类一旦具备Monad特性就可 ...

  10. Monad 系列

    本系列是在学习Monad时在网上找到的一个老外的博客,作者是MikeHadlow,地址是mikehadlow.blogspot.com,  可惜国内访问不了.这个系列对Monad讲解的浅显易懂,而且有 ...

随机推荐

  1. 理解与模拟一个简单web服务器

    先简单说下几个概念,根据自己的理解,不正确请见谅. web服务器 首先要知道什么是web服务器,简单说web服务器就是可以使用HTTP传输协议与客户端进行通信的服务器.最初的web服务器只能用来处理静 ...

  2. Ubuntu 14.04上安装caffe

    本来实在windows 10上尝试安装caffe,装了一天没装上,放弃; 改在windows上装ubuntu的双系统,装了一个下午,不小心windows的系统盘被锁死了,也不会unlock?只好含泪卸 ...

  3. Yii2的深入学习--入口文件

    前一段时间,尝试去写一个 php 的简单框架,发现自己还欠缺很多,就暂时停掉了.准备先读完 Yii2 的源码,然后再去看完 laravel 的源码,最后再继续去写这个简单的 php 框架. 之后关于 ...

  4. [OpenCV] Samples 05: convexhull

    得到了复杂轮廓往往不适合特征的检测,这里再介绍一个点集凸包络的提取函数convexHull,输入参数就可以是contours组中的一个轮廓,返回外凸包络的点集 ---- 如此就能去掉凹进去的边. 对于 ...

  5. Intention.js – 动态重构 HTML 为响应式模式

    Intention.js 提供一个轻量级的和明确的方式,帮助你动态重组 HTML,成为响应式的方式.操作方法都放在了元素自己里面,所以灵活的布局看起来就似乎不会那么的抽象和凌乱. 您可以轻松地增加布局 ...

  6. Javascript模块化开发,使用模块化脚本加载工具RequireJS,提高你代码的速度和质量。

    随着前端JavaScript代码越来越重,如何组织JavaScript代码变得非常重要,好的组织方式,可以让别人和自己很好的理解代码,也便于维护和测试.模块化是一种非常好的代码组织方式,本文试着对Ja ...

  7. 2.SDK目录结构和adb工具及命令介绍

    安卓开发学习笔记 1.安卓开发之环境搭建 2.SDK目录结构和adb工具及命令介绍 1.SDK目录介绍: ******************************** add-ons:Androi ...

  8. Android自动化测试之Monkeyrunner学习笔记(一)

    Android自动化测试之Monkeyrunner学习笔记(一) 因项目需要,开始研究Android自动化测试方法,对其中的一些工具.方法和框架做了一些简单的整理,其中包括Monkey.Monkeyr ...

  9. c#实现查询程序运行线程数

    class Program { static void Main(string[] args) { List<Thread> list = new List<Thread>() ...

  10. JavaScript可否多线程? 深入理解JavaScript定时机制

    JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如 setTimeout( ...