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. Atitit。木马病毒原理机密与概论以及防御

    Atitit.木马病毒原理机密与概论以及防御 1. 定时截屏木马1 1.1. QQ聊天与微信聊天木马1 2. 文档木马1 3. 病毒木马的触发方式2 4. 远程木马2 5. 漏洞木马2 6. 病毒木马 ...

  2. iOS-推送通知详解

    这是一篇编译的文章,内容均出自Parse.com的iOS开发教程,同时作者还提供了视频讲解.本文将带领开发者一步一步向着iOS推送通知的深处探寻,掌握如何配置iOS推送通知的奥义. 介绍一点点背景资料 ...

  3. MongoDB 简介

    MongoDB 简介 介绍:MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案.特点:高性能.易部署.易使用,存储数据非常方便.主要功 ...

  4. urldecode解码方法

    输入为带有urldecode转码文本,输出正常文本. //20130625 by zhangyl private string ConvertToString(string input) { inpu ...

  5. SQL Pass北京举行2014年第一次线下活动

    地点:北京微软(中国)有限公司[望京利星行],三层308室 时间:2014年 3 月15日 13:30-16:30 SQL PASS 北京QQ群号:2435349 报名地址:http://1drv.m ...

  6. 【WP 8.1开发】手机客户端应用接收推送通知

    上一篇文章中,已经完成了用于发送通知的服务器端,接下来我们就用这个服务端来测试一下. 在开始测试之前,我们要做一个接收通知的WP应用. 1.启动VS Express for Windows,新建项目, ...

  7. Xdebug 配置

    到官网 http://www.xdebug.com/download.php 下载 找到对应PHP版本的 Xdebug ,后面带 TS 的为线程安全,本机环境为 win7 64 + php-5.5.1 ...

  8. ZZUOJ1196: 单调数

    /* 注意的事项:是输出小于 10^n的正整数的个数哦!开始的时候总比样例输出多一个数, 纠结了好久,原来是 0加了进去了! dpI[n][m]表示的是第n位添加数字m(0....9)的构成单调递增数 ...

  9. java中Object.equals()简单用法

    /* equals()方法默认的比较两个对象的引用! */ class Child { int num; public Child(int x){ num = x; } //人文的抛出运行时异常的好处 ...

  10. MongoDB的学习--聚合

    最近要去的新项目使用mysql,趁着还没忘记,总结记录以下MongoDB的聚合. 聚合是泛指各种可以处理批量记录并返回计算结果的操作.MongoDB提供了丰富的聚合操作,用于对数据集执行计算操作.在  ...