在上面几期讨论中我们连续介绍了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. 安装Jenkins

    jenkins是一个广泛用于持续构建的可视化web工具,持续构建说得更直白点,就是各种项目的"自动化"编译.打包.分发部署的工具 1.jenkins的官网 2.jenkins各个系 ...

  2. javascript_core_09之继承、属性、对象

    1.OOP之修改继承: ①child._proto_=father:=>Object.setPrototypeOf(child,father):每次只能修改一个对象的父对象: ②构造函数.pro ...

  3. How Google TestsSoftware - Part Three

    Lots of questions in thecomments to the last two posts. I am not ignoring them. Hopefully many of th ...

  4. 分享.NET系统开发过程中积累的扩展方法

    .NET 3.5提供的扩展方法特性,可以在不修改原类型代码的情况下扩展它的功能.下面分享的这些扩展方法大部分来自于Code Project或是Stackoverflow,.NET为此还有一个专门提供扩 ...

  5. ASP.NET MVC 4使用PagedList.Mvc分页

    ASP.NET MVC中进行分页的方式有多种,在NuGet上有提供使用PagedList.PagedList.Mvc进行分页. 1. 通过NuGet引用PagedList.Mvc 在安装引用Paged ...

  6. 深入理解CSS浮动

    × 目录 [1]定义 [2]特性 [3]表现 [4]重叠 前面的话 浮动最早的使用是出自<img src="#" align="right">,用于 ...

  7. Unity3D 游戏前端开发技能树(思维导图)

    如果做游戏也是一种游戏,那么这个游戏的自由度实在是太高了.(导图源文件链接:http://pan.baidu.com/s/1eSHpH5o 密码:qzl5) 最近要用思维导图软件Xmind把自己的思路 ...

  8. 牛顿法与拟牛顿法学习笔记(四)BFGS 算法

    机器学习算法中经常碰到非线性优化问题,如 Sparse Filtering 算法,其主要工作在于求解一个非线性极小化问题.在具体实现中,大多调用的是成熟的软件包做支撑,其中最常用的一个算法是 L-BF ...

  9. Pig 实现关键词匹配

    1. 问题描述 收集日志avro数据中有两个Map字段appInstall.appUse分别表示已安装的app.正在使用的app,且key值为app的名称,value值为app使用信息.现在要得到一份 ...

  10. NPOI导入导出EXCEL通用类,供参考,可直接使用在WinForm项目中

    以下是NPOI导入导出EXCEL通用类,是在别人的代码上进行优化的,兼容xls与xlsx文件格式,供参考,可直接使用在WinForm项目中,由于XSSFWorkbook类型的Write方法限制,Wri ...