scalaz-stream是一个泛函数据流配件库(functional stream combinator library),特别适用于函数式编程。scalar-stream是由一个以上各种状态的Process串联组成。stream代表一连串的元素,可能是自动产生或者由外部的源头输入,如:一连串鼠标位置;文件中的文字行;数据库记录;又或者一连串的HTTP请求等。Process就是stream转换器(transducer),它可以把一种stream转换成另一种stream。Process的类型款式如下:

sealed trait Process[+F[_], +O]

其中F是个高阶类,是一种算法,O是Process的运算值。从类型款式上看Process是个对O类型值进行F运算的节点,那么scalaz-stream就应该是个运算流了。Process包含以下几种状态:

case class Emit[+O](seq: Seq[O]) extends HaltEmitOrAwait[Nothing, O] with EmitOrAwait[Nothing, O]

case class Await[+F[_], A, +O](
req: F[A]
, rcv: (EarlyCause \/ A) => Trampoline[Process[F, O]] @uncheckedVariance
, preempt : A => Trampoline[Process[F,Nothing]] @uncheckedVariance = (_:A) => Trampoline.delay(halt:Process[F,Nothing])
) extends HaltEmitOrAwait[F, O] with EmitOrAwait[F, O] {
...
}
case class Halt(cause: Cause) extends HaltEmitOrAwait[Nothing, Nothing] with HaltOrStep[Nothing, Nothing] case class Append[+F[_], +O](
head: HaltEmitOrAwait[F, O]
, stack: Vector[Cause => Trampoline[Process[F, O]]] @uncheckedVariance
) extends Process[F, O] {
...
}

scalaz-stream是个主动读取模式的流(pull model stream),Process转换stream的方式不是以Stream[I] => Stream[O]这种函数方式,而是一种状态转换方式进行(state transition),所以这些状态就等于向一个驱动程序发出的请求:

Emit[+O]:请求发一个O值

Await[+F[_],A,+O]:要求运算F[A],得出F[A]的结果A后输入函数rcv再运算得出下一个Process状态。这个是flatMap函数的结构化版本

Halt:停止发送

Append:连接前后两个Process

可以看到Emit,Await,Halt,Append都是Process类型的结构化状态。其中Await就是flatMap函数的结构化,Emit就像Return,所以Process就是一个Free Monad。

Emit的作用是发出一个O值,Await的作用是运算F然后连接下一个Process, Append的作用则是把前一个Process的信息传递到下一个Process。Await和Append分别是不同方式的Process连接方式。

Process又分以下几类:

  type Process0[+O] = Process[Nothing,O]

  /**
* A single input stream transducer. Accepts input of type `I`,
* and emits values of type `O`.
*/
type Process1[-I,+O] = Process[Env[I,Any]#Is, O] /**
* A stream transducer that can read from one of two inputs,
* the 'left' (of type `I`) or the 'right' (of type `I2`).
* `Process1[I,O] <: Tee[I,I2,O]`.
*/
type Tee[-I,-I2,+O] = Process[Env[I,I2]#T, O] /**
* A stream transducer that can read from one of two inputs,
* non-deterministically.
*/
type Wye[-I,-I2,+O] = Process[Env[I,I2]#Y, O] /**
* An effectful sink, to which we can send values. Modeled
* as a source of effectful functions.
*/
type Sink[+F[_],-O] = Process[F, O => F[Unit]] /**
* An effectful channel, to which we can send values and
* get back responses. Modeled as a source of effectful
* functions.
*/
type Channel[+F[_],-I,O] = Process[F, I => F[O]]

Process[F[_],O]:source:运算流源点,由此发送F[O]运算

Process0[+O]:>>>Process[Nothing,+O]:source:纯数据流源点,发送O类型元素

Process1[-I,+O]:一对一的数据转换节点:接收一个I类型输入,经过处理转换成O类型数据输出

Tee[-I1,-I2,+O]:二对一的有序输入数据转换节点:从左右两边一左一右有顺接受I1,I2类型输入后转换成O类型数据输出

Wye[-I1,-I2,+O]:二对一的无序输入数据转换节点:不按左右顺序,按上游数据发送情况接受I1,I2类型输入后转换成O类型数据输出

Sink[+F[_],-O]:运算终点,在此对O类型数据进行F运算,不返回值:O => F[Unit]

Channel[+F[_],-I,O]:运算终点,接受I类型输入,进行F运算后返回F[O]:I => F[O]

以下是一些简单的Process构建方法:

  Process.emit()                                  //> res0: scalaz.stream.Process0[Int] = Emit(Vector(1))
Process.emitAll(Seq(,,)) //> res1: scalaz.stream.Process0[Int] = Emit(List(1, 2, 3))
Process.halt //> res2: scalaz.stream.Process0[Nothing] = Halt(End)
Process.range(,,) //> res3: scalaz.stream.Process0[Int] = Append(Halt(End),Vector(<function1>))

这些是纯数据流的构建方法。scalaz-stream通常把Task作为F运算,下面是Task运算流的构建或者转换方法:

 val p: Process[Task,Int] = Process.emitAll(Seq(,,))  //> p  : scalaz.stream.Process[scalaz.concurrent.Task,Int] = Append(Halt(End),Vector(<function1>))
Process.range(,,).toSource //> res4: scalaz.stream.Process[scalaz.concurrent.Task,Int] = Append(Halt(End),Vector(<function1>))
//把F[A]升格成Process[F,A]
Process.eval(Task.delay { * }) //> res5: scalaz.stream.Process[scalaz.concurrent.Task,Int] = Await(scalaz.concurrent.Task@56aac163,<function1>,<function1>)

对stream的Process进行运算有下面几种run方法:

/**
* Collect the outputs of this `Process[F,O]` into a Monoid `B`, given a `Monad[F]` in
* which we can catch exceptions. This function is not tail recursive and
* relies on the `Monad[F]` to ensure stack safety.
*/
final def runFoldMap[F2[x] >: F[x], B](f: O => B)(implicit F: Monad[F2], C: Catchable[F2], B: Monoid[B]): F2[B] = {
...}
/**
* Collect the outputs of this `Process[F,O]`, given a `Monad[F]` in
* which we can catch exceptions. This function is not tail recursive and
* relies on the `Monad[F]` to ensure stack safety.
*/
final def runLog[F2[x] >: F[x], O2 >: O](implicit F: Monad[F2], C: Catchable[F2]): F2[Vector[O2]] = {
...}
/** Run this `Process` solely for its final emitted value, if one exists. */
final def runLast[F2[x] >: F[x], O2 >: O](implicit F: Monad[F2], C: Catchable[F2]): F2[Option[O2]] = {
...}
/** Run this `Process` solely for its final emitted value, if one exists, using `o2` otherwise. */
final def runLastOr[F2[x] >: F[x], O2 >: O](o2: => O2)(implicit F: Monad[F2], C: Catchable[F2]): F2[O2] =
runLast[F2, O2] map { _ getOrElse o2 }
/** Run this `Process`, purely for its effects. */
final def run[F2[x] >: F[x]](implicit F: Monad[F2], C: Catchable[F2]): F2[Unit] =
F.void(drain.runLog(F, C))

这几个函数都返回F2运算,如果F2是Task的话那么我们就可以用Task.run来获取结果值:

  //runFoldMap就好比Monoid的sum
p.runFoldMap(identity).run //> res6: Int = 6
p.runFoldMap(i => i * ).run //> res7: Int = 12
p.runFoldMap(_.toString).run //> res8: String = 123
//runLog把收到的元素放入vector中
p.runLog.run //> res9: Vector[Int] = Vector(1, 2, 3)
//runLast取最后一个元素,返回Option
p.runLast.run //> res10: Option[Int] = Some(3)
Process.halt.toSource.runLast.run //> res11: Option[Nothing] = None
Process.halt.toSource.runLastOr().run //> res12: Int = 65
//run只进行F的运算,放弃所有元素
p.run //> res13: scalaz.concurrent.Task[Unit] = scalaz.concurrent.Task@26b3fd41
p.run.run //Task[Unit] 返回Unit
Process.emit(print("haha")).toSource.run.run //> haha

与List和Stream操作相似,我们同样可以对scalar-stream Process施用同样的操作函数,也就是一些stream转换函数:

  p.take().runLog.run                             //> res14: Vector[Int] = Vector(1, 2)
p.filter {_ > }.runLog.run //> res15: Vector[Int] = Vector(3)
p.last.runLog.run //> res16: Vector[Int] = Vector(3)
p.drop().runLog.run //> res17: Vector[Int] = Vector(2, 3)
p.exists{_ > }.runLog.run //> res18: Vector[Boolean] = Vector(false)

以上这些函数与scala标准库的stream很相似。再看看map,flatMap吧:

  p.map{i => s"Int:$i"}.runLog.run                 //> res19: Vector[String] = Vector(Int:1, Int:2, Int:3)
p.flatMap{i => Process(i,i-)}.runLog.run //> res20: Vector[Int] = Vector(1, 0, 2, 1, 3, 2)

仔细检查可以看出来上面的这些转换操作都是针对Process1类型的,都是元素在流通过程中得到转换。我们会在下篇讨论中介绍一些更复杂的Process操作,如:Sink,Tee,Wyn...,然后是scalaz-stream的具体应用

Scalaz(46)- scalaz-stream 基础介绍的更多相关文章

  1. Node.js学习笔记(一)基础介绍

    什么是Node.js 官网介绍: Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js us ...

  2. Node.js 基础介绍

    什么是Node.js 官网介绍: Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js us ...

  3. OSPF基础介绍

    OSPF基础介绍 一.RIP的缺陷 1.以跳数评估的路由并非最优路径 2.最大跳数16导致网络尺度小 3.收敛速度慢 4.更新发送全部路由表浪费网络资源 二.OSPF基本原理 1.什么是OSPF a& ...

  4. Erlang基础 -- 介绍 -- Wordcount示例演示

    在前两个blog中,已经说了Erlang的历史.应用场景.特点,这次主要演示一个Wordcount的示例,就是给定一个文本文件,统计这个文本文件中的单词以及该单词出现的次数. 今天和群友们讨论了一个问 ...

  5. Web服务基础介绍

    Web服务基础介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.正常情况下的单次web服务访问流程 博主推荐阅读: https://www.cnblogs.com/yinzh ...

  6. XML基础介绍【一】

    XML基础介绍[一] 1.XML简介(Extensible Markup Language)[可扩展标记语言] XML全称为Extensible Markup Language, 意思是可扩展的标记语 ...

  7. Web3D编程入门总结——WebGL与Three.js基础介绍

    /*在这里对这段时间学习的3D编程知识做个总结,以备再次出发.计划分成“webgl与three.js基础介绍”.“面向对象的基础3D场景框架编写”.“模型导入与简单3D游戏编写”三个部分,其他零散知识 ...

  8. C++ 迭代器 基础介绍

    C++ 迭代器 基础介绍 迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围.迭代器就如同一个指针.事实上,C++的指针也是一种迭代器.但是,迭代器不仅仅是指针,因此你不能认为他们一定 ...

  9. 1、git基础介绍及远程/本地仓库、分支

    1. Git基础介绍 基于Git进行开发时,首先需要将远程仓库代码clone到本地,即为本地仓库.后续大部分时间都是基于本地仓库上的分支进行编码,最后将本地仓库的代码合入远程仓库. 1.1. 远程仓库 ...

  10. git基础介绍

    git基础介绍 这是git操作的基础篇,是以前的写的操作文档,就没有进行手打,直接把图片贴进来了,你们担待哈,有不正确的地方可以指正出来,我将在第一时间去修改,多谢哈! 一.文件状态:git系统的文件 ...

随机推荐

  1. JS中的宽高(基础知识很重要)

    IE中:document.body.clientWidth ==> BODY对象宽度document.body.clientHeight ==> BODY对象高度document.docu ...

  2. Atitit vod ver 12 new feature v12 pb2 影吧 视频 电影 点播 播放系统v12新特性

    Atitit vod ver 12 new feature v12 pb2 影吧 视频 电影 点播 播放系统v12新特性 项目分离从独立的se ver Run mode from brow ex to ...

  3. 1027 HTML

    body bgcolor(背景色)="#9900FF"(引号内呈现的是颜色代号,99是红色 00是绿色 FF是蓝色,所有颜色都是以这三个颜色调配) text (字体颜色)=&quo ...

  4. 使用node+vue.js实现SPA应用,nodevue.jsspa应用

    使用node+vue.js实现SPA应用,nodevue.jsspa应用 http://www.bkjia.com/Javascript/1097617.html https://github.com ...

  5. XML学习笔记7——XSD实例

    在前面的XSD笔记中,基本上是以数据类型为主线来写的,而在我的实际开发过程中,是先设计好了XML的结构(元素.属性),并写好了一份示例,然后再反过来写XSD文件(在工具生成的基础上修改),也就是说,是 ...

  6. String详解

    在开发中,我们都会频繁的使用String类,掌握String的实现和常用方法是必不可少的,当然,我们还需要了解它的内部实现. 一. String的实现 在Java中,采用了一个char数组实现Stri ...

  7. SDWebImage清除图片缓存

    背景: 使用 SDWebImage 库,由于内存中一直缓存着加载的图片,而导致内存过高(我们无法手动管理内存),弹出内存警告而导致程序很卡或者直接crash掉. 我的解决方法: 在AppDelegat ...

  8. ASP.NET MVC 4 Optimization的JS/CSS文件动态合并及压缩

    JS/CSS文件的打包合并(Bundling)及压缩(Minification)是指将多个JS或CSS文件打包合并成一个文件,并在网站发布之后进行压缩,从而减少HTTP请求次数,提高网络加载速度和页面 ...

  9. hibernate(十)双向关联关系的CRUD

    本文链接:http://www.orlion.ml/28/ 一.保存 1. 假设一个group有多个user,一个user只属于一个group,当保存user对象到数据库中时可以 User u = n ...

  10. IP,路由,交换基础培训记录

    IP 掩码  子网划分 vlan划分(有助于减少广播压力) vlan之间互通通过交换机打通. 路由,静态路由,动态路由(学习到的),路由表,下一跳,网络位长的优先级高. 交换机,hub集线器. hub ...