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. Kafka与Logstash的数据采集对接 —— 看图说话,从运行机制到部署

    基于Logstash跑通Kafka还是需要注意很多东西,最重要的就是理解Kafka的原理. Logstash工作原理 由于Kafka采用解耦的设计思想,并非原始的发布订阅,生产者负责产生消息,直接推送 ...

  2. Atitit usrqbg1834 html的逻辑化流程化 规范标准化解决方案

    Atitit usrqbg1834 html的逻辑化流程化 规范标准化解决方案 常用指令1 ..v-if.v-else指令2 v-for指令3 MVVM大比拼4 常用指令 本来按照Vue文档说明,常用 ...

  3. jeasyUI的treegrid批量删除多行(转载)

    看上去,JavaScript的变量类型,也可以分为值类型和引用类型.赋值操作中,值类型,各自独立,互不干涉:引用类型,指针而已,大家指向同一个对象. 为什么这样说呢? 我是从jeasyUI的treeg ...

  4. Sql Server系列:索引维护

    1. DBCC SHOWCONTIG 显示指定表的数据和索引的碎片信息.当对表进行大量的修改或添加数据后,执行此语句可以查看有无碎片,显示指定的表或试图的数据和索引的碎片信息. 其语法格式: DBCC ...

  5. SSIS连接SAPBI

        SSIS默认的连接管理器是没有连接到Oracle或SAPBI的,因此如果希望SSIS调用SAP RFC的话,可以使用微软提供的连接管理器插件.     有关这一点的详细信息可以参考MSDN的官 ...

  6. CSS背景100%平铺 浏览器缩小背景显示不全解决办法

    本文我们分享前端CSS背景100%平铺,浏览器缩小背景显示不全bug解决的两个方法,如果你也遇到了,那么就可以参考下面文章. 把浏览器的窗口缩小时,拖动滚动条时你会发现原本设定的CSS背景100%平铺 ...

  7. Representation Data in OpenCascade BRep

    Representation Data in OpenCascade BRep eryar@163.com 摘要Abstract:现在的显示器大多数是光栅显示器,即可以看做一个像素的矩阵.在光栅显示器 ...

  8. hdu3635 Dragon Balls(带权并查集)

    /* 题意:有N个城市, 每一个城市都有一个龙珠(编号与城市的编号相同),有两个操作 T A ,B 将标号为A龙珠所在城市的所有的龙珠移动到B龙珠所在城市中! 思路:并查集 (压缩路径的时候将龙珠移动 ...

  9. python--基础学习(一)开发环境搭建,体验HelloWorld

    python学习之前 最近想用python写爬虫,由于之前没接触过,所以从零开始,找了技术博文大概了解下基础. 印象比较深的是"python你不去认识它,可能没什么,一旦你认识了它,你就会爱 ...

  10. 在MVC5和webAPI下是用Autofac依赖注入

    很多书本中都提到依赖注入,控制反转等概念,这些都是为了实现松耦合层.组件和类目的. 常见的是使用Repository类分离Controller和Model的直接联系.而为了解除Repository类和 ...