Scalaz(54)- scalaz-stream: 函数式多线程编程模式-Free Streaming Programming Model
长久以来,函数式编程模式都被认为是一种学术研究用或教学实验用的编程模式。直到近几年由于大数据和多核CPU的兴起造成了函数式编程模式在一些实际大型应用中的出现,这才逐渐改变了人们对函数式编程无用论的观点。通过一段时间对函数式编程方法的学习,我们了解到Free Monad的算式/算法关注分离(separation of concern)可以是一种很实用的函数式编程模式。用Free Monad编写的程序容易理解并具备良好的可维护性。scalaz-stream的流程控制和多线程运算模式可以实现程序的安全并行运算。把Free Monad和scalaz-stream有机结合起来可以形成一种新的编程模式来支持函数式多线程编程来编制具备安全性、易扩展、易维护的并行运算程序。我们先从一个简单的Free Monad程序开始:
import scalaz._
import Scalaz._
import scalaz.concurrent._
import scalaz.stream._
import scala.language.higherKinds
import scala.language.implicitConversions
object freeStream {
//1. 定义语句
object DSLs {
sealed trait Interact[A]
case class Ask(q: String) extends Interact[String]
case class Tell(m: String) extends Interact[Unit]
//2. Free升格
implicit def interactToFree[A](ia: Interact[A]) = Free.liftF(ia)
}
//3. 程序逻辑/算式
object PRGs {
import DSLs._
val prgGetName: Free[Interact,Unit] = for {
first <- Ask("What's your first name?")
last <- Ask("What's your last name?")
_ <- Tell(s"Hello $first $last")
} yield ()
}
//4. 实现方式/算式
object IMPs {
import DSLs._
object InteractConsole extends (Interact ~> Id) {
def apply[A](ia: Interact[A]): Id[A] = ia match {
case Ask(q) => {println(q); Console.readLine}
case Tell(m) => println(m)
}
}
}
在这个程序里我们按照一个固定的框架步骤来实现“定义语句”、“升格Free”、“功能描述”及“实现方式”。这里特别需要注意的是所谓的算式/算法关注分离,即“功能描述”和“实现方式”是互不关联的。这样我们可以提供不同版本的实现方式来进行测试、环境转换等工作。Free Monad的具体运算方式如下:
//5. 运算/Run
import DSLs._,PRGs._,IMPs._
prgGetName.foldMapRec(InteractConsole)
运算结果返回A:对于prgGetName来说就是Unit。不过如果直接运行foldMapRec有可能会产生副作用(siede effect)。这样不符合纯代码要求,无法实现这个程序与其它程序的函数组合。我们需要把这段可能产生副作用的代码放到Task里:
val taskGetName = Task.delay { prgGetName.foldMapRec(InteractConsole)}
//> taskGetName : scalaz.concurrent.Task[scalaz.Scalaz.Id[Unit]] = scalaz.concurrent.Task@282ba1e
这样我们就获得了一个异线程的延迟运算。我们可以放心地用这个taskGetName进行函数组合。把这个Free Monad程序转换成scalaz-stream的Process也很容易:
val prcGetName = Process.eval(taskGetName) //> prcGetName : scalaz.stream.Process[scalaz.concurrent.Task,scalaz.Scalaz.Id[Unit]] = Await(scalaz.concurrent.Task@282ba1e,<function1,<function1>)
我们用Process.eval直接把它转换成Process[Task,Unit]类型。下面我们用scalaz-stream的运算方式来运算这个Free Monad程序:
object FreeInteract extends App {
import DSLs._,PRGs._,IMPs._
val taskGetName = Task.delay { prgGetName.foldMapRec(InteractConsole)}
val prcGetName = Process.eval(taskGetName)
prcGetName.run.run
}
运算结果如下:
What's your first name?
tiger
What's your last name?
chan
Hello, tiger chan!
虽然这个例子看起来很简单,但其中代表的意义却不小:我们潜移默化地实现了函数式多线程编程了。
如果我们需要Free Monad程序返回运算结果的话就调整一下功能描述(算式):
val prgGetUserID = for {
uid <- ask("Enter User ID:")
} yield uid
再运算一下:
object FreeInteract extends App {
import DSLs._,PRGs._,IMPs._
val taskGetName = Task.delay { prgGetName.foldMapRec(InteractConsole)}
val prcGetName = Process.eval(taskGetName)
//prcGetName.run.run
Process.eval(Task.delay{prgGetUserID.foldMapRec(InteractConsole)}).runLog.run.map(println)
...
Enter User ID:
tiger123
tiger123
用纯代码方式echo输入:
pUserID.evalMap { uid => Task.delay {prgEchoInput(uid).foldMapRec(InteractConsole)} }.run.run
...
Enter User ID:
user234
user234
也可以把结果发送到一个Sink来显示:
val outSink: Sink[Task,String] = Process.constant{x =>Task.delay{prgEchoInput(x).foldMapRec(InteractConsole)}}
(pUserID to outSink).run.run
...
Enter User ID:
jonathon
jonathon
我们试着再加一个Free程序功能:验证用户编号
sealed trait Login[A]
case class CheckID(id: String) extends Login[Boolean]
...
def prgCheckID(id: String) = for {
b <- Free.liftF(CheckID(id))
} yield b
...
object UserLogin extends (Login ~> Id) {
def apply[A](la: Login[A]): Id[A] = la match {
case CheckID(id) => if (id === "tiger123") true else false
}
}
stream流程是:先读取用户编号然后验证,跟着在Sink输出结果:
def fCheckID: String => Task[String] = id => Task.delay { prgCheckID(id).foldMapRec(UserLogin) }.map(_.toString)
val chCheckID = channel.lift(fCheckID)
((pUserID through chCheckID) to outSink).run.run
...
Enter User ID:
tiger123
true
...
Enter User ID:
johnny234
false
不错!Free Monad和scalar-stream可以很好的集成在一起。
我把这节讨论的示范源代码提供给大家:
import scalaz._
import Scalaz._
import scalaz.concurrent._
import scalaz.stream._
object DSLs {
sealed trait Interact[A]
case class Ask(q: String) extends Interact[String]
case class Tell(m: String) extends Interact[Unit]
object Interact {
def ask(q: String): Free[Interact, String] = Free.liftF(Ask(q))
def tell(m: String): Free[Interact, Unit] = Free.liftF(Tell(m))
}
sealed trait Login[A]
case class CheckID(id: String) extends Login[Boolean]
}
object PRGs {
import DSLs._
import Interact._ val prgGetName = for {
first <- ask("What's your first name?")
last <- ask("What's your last name?")
_ <- tell(s"Hello, $first $last!")
} yield() val prgGetUserID = for {
uid <- ask("Enter User ID:")
} yield uid def prgEchoInput(m: String) = tell(m) def prgCheckID(id: String) = for {
b <- Free.liftF(CheckID(id))
} yield b }
object IMPs {
import DSLs._
object InteractConsole extends (Interact ~> Id) {
def apply[A](ia: Interact[A]): Id[A] = ia match {
case Ask(q) => { println(q); readLine }
case Tell(m) => println(m)
}
}
object UserLogin extends (Login ~> Id) {
def apply[A](la: Login[A]): Id[A] = la match {
case CheckID(id) => if (id === "tiger123") true else false
}
}
} object FreeInteract extends App {
import DSLs._,PRGs._,IMPs._
val taskGetName = Task.delay { prgGetName.foldMapRec(InteractConsole)}
val prcGetName = Process.eval(taskGetName)
//prcGetName.run.run
val pUserID= Process.eval(Task.delay{prgGetUserID.foldMapRec(InteractConsole)})
//pUserID.evalMap { uid => Task.delay {prgEchoInput(uid).foldMapRec(InteractConsole)} }.run.run
val outSink: Sink[Task,String] = Process.constant { x => Task.delay {prgEchoInput(x).foldMapRec(InteractConsole) } }
//(pUserID to outSink).run.run
def fCheckID: String => Task[String] = id => Task.delay { prgCheckID(id).foldMapRec(UserLogin) }.map(_.toString)
val chCheckID = channel.lift(fCheckID)
((pUserID through chCheckID) to outSink).run.run
Scalaz(54)- scalaz-stream: 函数式多线程编程模式-Free Streaming Programming Model的更多相关文章
- Java多线程编程模式实战指南(三):Two-phase Termination模式
停止线程是一个目标简单而实现却不那么简单的任务.首先,Java没有提供直接的API用于停止线程.此外,停止线程时还有一些额外的细节需要考虑,如待停止的线程处于阻塞(等待锁)或者等待状态(等待其它线程) ...
- Java多线程编程模式实战指南(三):Two-phase Termination模式--转载
本文由本人首次发布在infoq中文站上:http://www.infoq.com/cn/articles/java-multithreaded-programming-mode-two-phase-t ...
- .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)
本文内容 异步编程类型 异步编程模型(APM) 参考资料 首先澄清,异步编程模式(Asynchronous Programming Patterns)与异步编程模型(Asynchronous Prog ...
- Scalaz(45)- concurrency :Task-函数式多线程编程核心配件
我们在上一节讨论了scalaz Future,我们说它是一个不完善的类型,最起码没有完整的异常处理机制,只能用在构建类库之类的内部环境.如果scalaz在Future类定义中增加异常处理工具的话,用户 ...
- Java多线程编程模式实战指南:Active Object模式(上)
Active Object模式简介 Active Object模式是一种异步编程模式.它通过对方法的调用与方法的执行进行解耦来提高并发性.若以任务的概念来说,Active Object模式的核心则是它 ...
- Java多线程编程模式实战指南(二):Immutable Object模式
多线程共享变量的情况下,为了保证数据一致性,往往需要对这些变量的访问进行加锁.而锁本身又会带来一些问题和开销.Immutable Object模式使得我们可以在不使用锁的情况下,既保证共享变量访问的线 ...
- Java多线程编程模式实战指南一:Active Object模式(上)
Active Object模式简介 Active Object模式是一种异步编程模式.它通过对方法的调用与方法的执行进行解耦来提高并发性.若以任务的概念来说,Active Object模式的核心则是它 ...
- Java多线程编程模式实战指南之Promise模式
Promise模式简介(转) Promise模式是一种异步编程模式 .它使得我们可以先开始一个任务的执行,并得到一个用于获取该任务执行结果的凭据对象,而不必等待该任务执行完毕就可以继续执行其他操作.等 ...
- java多线程编程模式
前言 区别于java设计模式,下面介绍的是在多线程场景下,如何设计出合理的思路. 不可变对象模式 场景 1. 对象的变化频率不高 每一次变化就是一次深拷贝,会影响cpu以及gc,如果频繁操作会影响性能 ...
随机推荐
- 在 lua 中实现函数的重载
在 lua 中实现函数的重载.注:好吧,lua中原来可以实现重载...local function create() local arg_table = {} local function dispa ...
- 每天一个linux命令(40):wc命令
Linux系统中的wc(Word Count)命令的功能为统计指定文件中的字节数.字数.行数,并将统计结果显示输出. 1.命令格式: wc [选项]文件... 2.命令功能: 统计指定文件中的字节数. ...
- 深入入门系列--Data Structure--04树
终于有机会重新回头学习一下一直困扰自身多年的数据结构了,赶脚棒棒哒.一直以来,对数据结构的掌握基本局限于线性表,稍微对树有一丢丢了解,而对于图那基本上就是不懂(不可否认,很多的考试中回避了图也是原因之 ...
- 快速入门系列--WCF--04元数据和异常处理
本章节将进行元数据和异常处理的介绍,这部分内容概念型比较强,可以快速浏览一下就好. 客户端和服务器借助于终结点进行通信,服务的提供者通过一个或者多个终结点将服务发布出来,服务的消费者则通过创建与之匹配 ...
- 原生JS实现分页效果1.0
不太完整,写的太急,等等加上完整注释,写起来还是有些难度的,写的有点水,后面再改进改进. <!DOCTYPE html><html lang="en">&l ...
- 【WP开发】WebView控件应用要点
WebView控件我就不多作详细的介绍,相信大家都懂的,就算你没用过,你看他的名字和长相都知道它的用途了.就是用来显示HTML内容的. 在WP 8.1的Runtime App中,这个控件大致有以下几个 ...
- Android开发之注册登录
昨天给大家介绍了一下关于Android端向服务器端发送数据的方法,不过貌似有一点瑕疵,今天经过调试已经解决,在这里给大家介绍一下 貌似Android4.0以后版本的对于网络权限要求变得严格,导致昨天编 ...
- 解决firefox和IE9对icon font字体的跨域访问问题
何为跨域访问,为什么会有跨域限制?一切还得从浏览器的同源策略说起. 同源策略:是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能会受到影响,可以说Web是构建在同源策略基础 ...
- PHP API接口测试小工具
前端时间给手机客户端做接口,当时弱爆了,写完API接口后,也不怎么测试,最后是等客户端调用的时候检验API的正确性. 后面利用PHP的curl实现Post请求,检验API接口的正确性:配合前面做的一个 ...
- iOS_UIImge_Gif的展示
github地址: https://github.com/mancongiOS/UIImage.git 方式一: 用动画展示 UIImageView *gifImageView = [[UIImage ...