在上面几期讨论中我们连续介绍了Free Monad。因为FP是纯函数编程,也既是纯函数的组合集成,要求把纯代码和副作用代码可以分离开来。Free Monad的程序描述(AST)和程序实现(Interpretation)关注分离(separation of concern)模式恰恰能满足FP要求。我们可以用一些代数数据类型(ADT Algebraic Data Type)来模拟功能,再把这些ADT组合起来形成AST(Abstract Syntax Tree)。AST既是对程序功能的描述,它的组成过程也就是Monadic Programming了。在另外一个过程中,我们可以按需要去实现各种Interpreter,从而达到实际运算的目的。我认为既然FP也被称为Monadic Programming,那么Free Monad应该是FP里最重要的数据结构,它的应用模式代表了主流FP,应该有个规范的具体使用方式。在本次讨论中我们将会集中对Free Monad的应用模式进行示范体验。

我们在这次示范中模拟一个针对键值存储(Key Value Store)的操作例子:

1、ADT设计

 sealed trait KVS[+Next]
object KVS {
case class Get[Next](key: String, onValue: String => Next) extends KVS[Next]
case class Put[Next](key: String, value: String, n: Next) extends KVS[Next]
case class Del[Next](key: String, n: Next) extends KVS[Next]

KVS[+Next]就是一种F[A]类型。从Suspend[F[Free[F,A]]可以得出A类型即Free类型,那么Next就是一个Free类,代表Free的下一个状态。如果需要使用Next,F[_]必须是个Functor, 这样才能通过F.map(A=>B)来获取F[B],B==另一个Free。 Put,Del模拟了无返回结果指令,那么如果需要链接到下一个Free状态的话就直接把一个Free放人Next位置。Get返回一个String,onValue函数接过这个返回值再连接到下一个Free状态。

2、获取Functor实例

   implicit val kvsFunctor = new Functor[KVS] {
def map[A,B](kvs: KVS[A])(f: A => B): KVS[B] = kvs match {
case Get(key, onResult) => Get(key, onResult andThen f)
case Put(key, value, next) => Put(key,value,f(next))
case Del(key,next) => Del(key,f(next))
}
}

把A转换成B就是把Free[KVS,A]转成Free[KVS,B],其实就是map over Next,对Next进行转换。对于函数C=>Next,map就是函数组合了:(C=>Next) andThen (Next=>B)。

3、类型升格,lift to Free

 implicit def kvsToFree[A](ka: KVS[A]): Free[KVS,A] = Free.liftF(ka)
def put(key: String , value: String): Free[KVS,Unit] = Free.liftF(Put(key,value,()))
def get(key: String): Free[KVS,String] = Free.liftF(Get(key,identity))
def del(key: String): Free[KVS,Unit] = Free.liftF(Del(key,()))

包括隐式类型转换kvsToFree,可以把任何KVS[A]升格成Free[KVS,A]。独立指令升格put,get,del,因为不涉及下一个状态所以使用了()和identity。

4、Composition,Free Monad组合

 import KVS._
def modify(key: String, f: String => String): Free[KVS,Unit] =
for {
v <- Get(key,identity)
_ <- Put(key,f(v), ())
} yield() //> modify: (key: String, f: String => String)scalaz.Free[Exercises.freeExamples.KVS,Unit]

通过隐式函数kvsToFree把ADT Get,Put升格成Free[KVS,A],然后实现函数组合。

5、功能描述,AST设计

 val script = for {
_ <- put("USA","United States Of America")
_ <- put("CHN","China")
_ <- put("PIL","Pilipines")
_ <- put("JPN","Japan")
_ <- modify("CHN",_ =>"People's Republic Of China")
_ <- del("PIL")
chn <- get("CHN")
} yield chn //> script : scalaz.Free[Exercises.freeExamples.KVS,String] = Gosub()

使用的是独立直接升格指令函数。函数直接返回了Free类型。就像是在for-loop里进行我们熟悉的行令编程:逐条指令编写。

6、功能实现,Interpretation

a、尾递归编译,tail-recursive interpretation

 def foldScript(kvs: Free[KVS,String],table: Map[String,String] = Map.empty): Map[String,String] =
kvs.resume.fold (
{
case Get(key,onResult) => foldScript(onResult(table(key)), table)
case Put(key,value, next) => foldScript(next, table + (key -> value))
case Del(key,next) => foldScript(next, table - key)
},
_ => table
) //> foldScript: (kvs: scalaz.Free[Exercises.freeExamples.KVS,String], table: Map[String,String])Map[String,String]
foldScript(script,Map.empty) //> res0: Map[String,String] = Map(USA -> United States Of America, CHN -> People's Republic Of China, JPN -> Japan)

注意,fold其实是Either.fold。foldScript是个尾递归函数。这时候Next就成为下一步递归的链接了。

b、foldMap,高阶类型转换,Natural Transformation,F[A]~>G[A]

 type KVState[A] = State[Map[String,String],A]
object KvsToMap extends (KVS ~> KVState) {
def apply[A](kvs: KVS[A]): KVState[A] = kvs match {
case Get(key,onResult) => State { m => (m, onResult(m(key))) }
case Put(key,value,next) => State { m => (m + (key -> value), next) }
case Del(key,next) => State { m => (m - key, next) }
}
}
script.foldMap(KvsToMap).run(Map.empty) //> res1: scalaz.Id.Id[(Map[String,String], String)] = (Map(USA -> United States Of America, CHN -> People's Republic Of China, JPN -> Japan),People's Republic Of China)

c、mutable实现方法:

 def goScript(kvs: Free[KVS,String],table: scala.collection.mutable.Map[String,String]):Unit =
kvs.go {
case Get(key,onResult) => onResult(table(key))
case Put(key,value,next) => table += (key -> value); next
case Del(key,next) => table -= key; next
} //> goScript: (kvs: scalaz.Free[Exercises.freeExamples.KVS,String], table: scala.collection.mutable.Map[String,String])Unit
val mutableMap = scala.collection.mutable.Map[String,String]()
//> mutableMap : scala.collection.mutable.Map[String,String] = Map()
goScript(script,mutableMap)
println(mutableMap) //> Map(JPN -> Japan, CHN -> People's Republic Of China, USA -> United States Of America)

把完整的示范源代码提供给大家:

 package Exercises
import scalaz._
import Scalaz._
import scala.language.higherKinds
import scala.language.implicitConversions
object freeExamples {
sealed trait KVS[+Next]
object KVS {
case class Get[Next](key: String, onValue: String => Next) extends KVS[Next]
case class Put[Next](key: String, value: String, n: Next) extends KVS[Next]
case class Del[Next](key: String, n: Next) extends KVS[Next]
implicit val kvsFunctor = new Functor[KVS] {
def map[A,B](kvs: KVS[A])(f: A => B): KVS[B] = kvs match {
case Get(key, onResult) => Get(key, onResult andThen f)
case Put(key, value, next) => Put(key,value,f(next))
case Del(key,next) => Del(key,f(next))
}
}
implicit def kvsToFree[A](ka: KVS[A]): Free[KVS,A] = Free.liftF(ka)
def put(key: String , value: String): Free[KVS,Unit] = Free.liftF(Put(key,value,()))
def get(key: String): Free[KVS,String] = Free.liftF(Get(key,identity))
def del(key: String): Free[KVS,Unit] = Free.liftF(Del(key,()))
}
import KVS._
def modify(key: String, f: String => String): Free[KVS,Unit] =
for {
v <- Get(key,identity)
_ <- Put(key,f(v), ())
} yield()
val script = for {
_ <- put("USA","United States Of America")
_ <- put("CHN","China")
_ <- put("PIL","Pilipines")
_ <- put("JPN","Japan")
_ <- modify("CHN",_ =>"People's Republic Of China")
_ <- del("PIL")
chn <- get("CHN")
} yield chn def foldScript(kvs: Free[KVS,String],table: Map[String,String] = Map.empty): Map[String,String] =
kvs.resume.fold (
{
case Get(key,onResult) => foldScript(onResult(table(key)), table)
case Put(key,value, next) => foldScript(next, table + (key -> value))
case Del(key,next) => foldScript(next, table - key)
},
_ => table
)
foldScript(script,Map.empty) type KVState[A] = State[Map[String,String],A]
object KvsToMap extends (KVS ~> KVState) {
def apply[A](kvs: KVS[A]): KVState[A] = kvs match {
case Get(key,onResult) => State { m => (m, onResult(m(key))) }
case Put(key,value,next) => State { m => (m + (key -> value), next) }
case Del(key,next) => State { m => (m - key, next) }
}
}
script.foldMap(KvsToMap).run(Map.empty) def goScript(kvs: Free[KVS,String],table: scala.collection.mutable.Map[String,String]):Unit =
kvs.go {
case Get(key,onResult) => onResult(table(key))
case Put(key,value,next) => table += (key -> value); next
case Del(key,next) => table -= key; next
}
val mutableMap = scala.collection.mutable.Map[String,String]()
goScript(script,mutableMap)
println(mutableMap)
}

Scalaz(36)- Free :实践-Free In Action - 实用体验的更多相关文章

  1. 编写自己的 GitHub Action,体验自动化部署

    本文将介绍如何使用 GitHub Actions 部署前端静态页面,以及如何自己创建一个 Docker 容器 Action. 简介 Actions GitHub Actions 是 GitHub 官方 ...

  2. Mono for Andriod学习与实践(1)— 初体验

    对于Andriod的开发者来说,相信Java语言是第一选择,可是对于.Net开发者来说,要想利用C#在Andriod平台上开发,Mono提供了相应的开发平台来实现,Mono for Andriod就是 ...

  3. LXC学习实践(3)快速体验第一个容器

    1.搭建第一个 LXC 虚拟计算机 #yum install lxc* 2.安装软件包后要检查 Linux 发行版的内核对 LXC 的支持情况,可以使用下面命令 #lxc-checkconfig #l ...

  4. 腾讯健康码16亿亮码背后的Elasticsearch系统调优实践【>>戳文章免费体验Elasticsearch服务30天】

    [活动]Elasticsearch Service免费体验馆>>Elasticsearch Service新用户特惠狂欢低至4折>>Elasticsearch Service企 ...

  5. 2018-2019-2 网络对抗技术 20165325 Exp3 免杀原理与实践

    2018-2019-2 网络对抗技术 20165325 Exp3 免杀原理与实践 实验内容(概要) 一.正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion,自己 ...

  6. 卓越Code团队SCRUM呕心沥血实践总结

    卓越Code团队SCRUM呕心沥血实践总结 序言 所属课程 https://edu.cnblogs.com/campus/xnsy/2019autumnsystemanalysisanddesign ...

  7. scrum-master个人实践回顾总结

    个人回顾总结 一.开课提出问题 第一次博客地址:https://www.cnblogs.com/Slow-Walker/p/11513179.html 二.问题回答 2.1问题1:针对单元测试 怎么保 ...

  8. 2016-2017-2 20155322 实验三 敏捷开发与XP实践

    2016-2017-2 20155322 实验三 敏捷开发与XP实践 实验内容 XP基础 XP核心实践 相关工具 实验知识点 敏捷开发(Agile Development)是一种以人为核心.迭代.循序 ...

  9. cropbox

    今天给大家分享一款基于jQuery头像裁剪插件cropbox,这是一款简单实用的jQuery头像在线裁剪插件.该插件适用于适用浏览器:IE8.360.FireFox.Chrome.Safari.Ope ...

随机推荐

  1. 锋利的JQuery —— 事件和动画

    大图猛戳

  2. Java编程思想(Chapter2、4、6)

    一切皆对象 用引用操纵对象 Java中操纵的标识符实际上是对象的“引用”.例如想要操纵一个字符串,则可以创建一个String引用. String s; 此处s只是一个引用. 存储位置 基本类型/对象的 ...

  3. Java 线程 — AbstractQueuedSynchronizer

    锁 锁就是一种状态,比如互斥锁:同一时间只能有一个线程拥有,可以使用一个整型值来标志当前的状态 0:表示没有现成占有锁 1:表示锁已经被占用 AbstractQueuedSynchronizer 实现 ...

  4. 有jQuery背景,该如何用AngularJS编程思想?

    "我可以熟练使用jQuery进行客户端应用的开发,但是现在我希望开始使用Angular.js.哪位能描述一下这个过程中必要的模式变化吗?希望您的答案能够围绕下面这些具体的问题: 1. 我如何 ...

  5. QQ5.0左侧滑动显示效果

    前三篇为大家介绍了如何实现简单的类QQ5.0左侧的侧滑效果,本篇我将带领大家一起探讨一下如何真正实现QQ5.0左侧的侧滑效果,对于本篇的内容与之前的三篇关联性很强,如果前三篇你已经完全掌握,对于这一篇 ...

  6. 34988 Happy Reversal(二进制去取反)

    /* 题意:给多个二进制数,对某些数进行按位取反操作! 然后从中找到最大数和最小数,并输出他们的差值! 注意:所有的数都是整数,包括取反之后 思路:一个n为二进制数x,令tmp为n位全1!则 y=tm ...

  7. python--爬虫入门(七)urllib库初体验以及中文编码问题的探讨

    python系列均基于python3.4环境 ---------@_@? --------------------------------------------------------------- ...

  8. python--基础学习(三)字符串单引号、双引号、三引号

    1.基本认识 单引号字符串:'python' 双引号字符串:"python" 三引号字符串:'''python'''(三单引号),"""python& ...

  9. java的LINQ :Linq4j简明介绍

    开发JAVA一段时间,面临的一大问题就是集合操作,习惯了LINQ的简洁语法,对JAVA的集合操作实在是无甚好感,只能通过C系的循环实现筛选等操作,由于没有延迟执行特性,内存占用实在不敢恭维.因此便在网 ...

  10. SQL*Loader之CASE8

    CASE8 1. SQL脚本 [oracle@node3 ulcase]$ cat ulcase8.sql set termout off rem host write sys$output &quo ...