在上面几期讨论中我们连续介绍了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. 解析for循环

    循环的作用就是让一个程序.连续进行一遍又一遍的循环: for循环: 分为四大类: 初始状态:相当于他一开始的数值,或条件: 循环条件:满足进行循环,不满足则停止: 循环体:循环的东西,程序: 状态改变 ...

  2. SQLServer查看死锁

    SQLServer查看死锁 if exists ( select * from sys.procedures where name like '%USP_ShowLocks%' ) drop proc ...

  3. hibernate(八) Hibernate检索策略(类级别,关联级别,批量检索)详解

    序言 很多看起来很难的东西其实并不难,关键是看自己是否花费了时间和精力去看,如果一个东西你能看得懂,同样的,别人也能看得懂,体现不出和别人的差距,所以当你觉得自己看了很多书或者学了很多东西的时候,你要 ...

  4. WPF自定义控件与样式(12)-缩略图ThumbnailImage /gif动画图/图片列表

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要针对WPF项目 ...

  5. Windows Azure Cloud Service (38) 微软IaaS与PaaS比较

    <Windows Azure Platform 系列文章目录> 最近一直想总结Azure IaaS和PaaS的区别与比较,写个博文详细说明一下.建议读者在阅读之前,先熟悉微软PaaS和Ia ...

  6. [SDK2.2]Windows Azure Virtual Network (4) 创建Web Server 001并添加至Virtual Network

    <Windows Azure Platform 系列文章目录> 在上一章内容中,笔者已经介绍了以下两个内容: 1.创建Virtual Network,并且设置了IP range 2.创建A ...

  7. 用Qt写软件系列五:一个安全防护软件的制作(1)

    引言 又有许久没有更新了.Qt,我心爱的Qt,为了找工作不得不抛弃一段时间,业余时间来学一学了.本来计划要写一系列关于Qt组件美化的博文,但是写了几篇之后就没坚持下去了.技术上倒是问题不大,主要是时间 ...

  8. 关于MVC中模型model的验证问题

    今天在做项目练习的时候发现,MVC中使用自带的模型验证时会提前显示在界面上,比如下面所示: 这是什么原因了,是因为我在表示get请求的action里面返回了其界面所显示使用的model,我们知道mvc ...

  9. git clone 失败问题解决方案

    第一次从github上通过终端pull项目,出现了上述问题.询问了后台,才知道原来是电脑公钥(publickey)未添加至github,所以无法识别. 因而需要获取本地电脑公钥,然后登录github账 ...

  10. C# 3DES加密

    最近一个项目中,因为服务端是用的java开发的,客户端是用的C#,由于通信部分采用到了3DES加密,所以做个记录,以备以后需要的时候直接用. 这是对方(java)的加密算法,和网上流传的代码也差不多( ...