在上面几期讨论中我们连续介绍了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. Atitit. Api 设计 原则 ---归一化

    Atitit. Api 设计 原则 ---归一化 1.1. 叫做归一化1 1.2. 归一化的实例:一切对象都可以序列化/toString  通过接口实现1 1.3. 泛文件概念.2 1.4. 游戏行业 ...

  2. Change Git Default Editor in Windows

    On 32 bit Win OS: git config --global core.editor "'C:/Program Files/Notepad++/notepad++.exe' - ...

  3. Linux快速入门02-文件系统管理

    继续进入Linux文件系统的学习,加油,早日突破MS压在自己身上的那道束缚. Linux系列文章 快速入门系列--Linux--01基础概念 快速入门系列--Linux--02文件系统管理 快速入门系 ...

  4. Oracle 11g系列:SQL Plus与PL/SQL

    SQL Plus是Oracle提供的一种用户接口,是数据库与用户之间进行交互的工具. PL/SQL是Oracle的过程化编程语言. 1.登录SQL Plus 利用Windows的[开始]|[运行]命令 ...

  5. 【Hibernate】一级、二级缓冲

    Hibernate缓冲按级别共分为两种,一级缓冲(Session)和二级缓冲(SessionFactory),有的也说是三种,还有一种是查询缓冲,当然,查询缓冲是依托于二级缓冲. ok,什么是缓冲? ...

  6. Android线程之并发处理

    上一篇为大家介绍了关于Looper的简单知识,本篇我们介绍一下多线程的并发处理,我们知道Handler通过sendMessage()发送的消息,首先发送给了Looper,存入Looper的消息栈,之后 ...

  7. NYOJ 1023 还是回文(DP,花最少费用形成回文串)

    /* 题意:给出一串字符(全部是小写字母),添加或删除一个字符,都会产生一定的花费. 那么,将字符串变成回文串的最小花费是多少呢? 思路:如果一个字符串增加一个字符 x可以形成一个回文串,那么从这个字 ...

  8. struts深入原理之RequestProcessor与xml

    和配置文件相对应的代码(struts1) public void process(HttpServletRequest request, HttpServletResponse response)   ...

  9. 轻松自动化---selenium-webdriver(python) (四)

    本节要解决的问题: 如何定位一组元素? 场景 从上一节的例子中可以看出,webdriver可以很方便的使用findElement方法来定位某个特定的对象,不过有时候我们却需要定位一组对象, 这时候就需 ...

  10. 10个实用的 CSS3 按钮效果制作教程

    人们往往喜欢那些有更多互动元素的网站,因此现在很多设计师专注于他们的 CSS3 技能.这是因为 CSS3 技能可以帮助他们在很大的程度上实现所需的吸引力.这里分享的10个优秀的 CSS3 按钮效果制作 ...