Scalaz(36)- Free :实践-Free In Action - 实用体验
在上面几期讨论中我们连续介绍了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 - 实用体验的更多相关文章
- 编写自己的 GitHub Action,体验自动化部署
本文将介绍如何使用 GitHub Actions 部署前端静态页面,以及如何自己创建一个 Docker 容器 Action. 简介 Actions GitHub Actions 是 GitHub 官方 ...
- Mono for Andriod学习与实践(1)— 初体验
对于Andriod的开发者来说,相信Java语言是第一选择,可是对于.Net开发者来说,要想利用C#在Andriod平台上开发,Mono提供了相应的开发平台来实现,Mono for Andriod就是 ...
- LXC学习实践(3)快速体验第一个容器
1.搭建第一个 LXC 虚拟计算机 #yum install lxc* 2.安装软件包后要检查 Linux 发行版的内核对 LXC 的支持情况,可以使用下面命令 #lxc-checkconfig #l ...
- 腾讯健康码16亿亮码背后的Elasticsearch系统调优实践【>>戳文章免费体验Elasticsearch服务30天】
[活动]Elasticsearch Service免费体验馆>>Elasticsearch Service新用户特惠狂欢低至4折>>Elasticsearch Service企 ...
- 2018-2019-2 网络对抗技术 20165325 Exp3 免杀原理与实践
2018-2019-2 网络对抗技术 20165325 Exp3 免杀原理与实践 实验内容(概要) 一.正确使用msf编码器,msfvenom生成如jar之类的其他文件,veil-evasion,自己 ...
- 卓越Code团队SCRUM呕心沥血实践总结
卓越Code团队SCRUM呕心沥血实践总结 序言 所属课程 https://edu.cnblogs.com/campus/xnsy/2019autumnsystemanalysisanddesign ...
- scrum-master个人实践回顾总结
个人回顾总结 一.开课提出问题 第一次博客地址:https://www.cnblogs.com/Slow-Walker/p/11513179.html 二.问题回答 2.1问题1:针对单元测试 怎么保 ...
- 2016-2017-2 20155322 实验三 敏捷开发与XP实践
2016-2017-2 20155322 实验三 敏捷开发与XP实践 实验内容 XP基础 XP核心实践 相关工具 实验知识点 敏捷开发(Agile Development)是一种以人为核心.迭代.循序 ...
- cropbox
今天给大家分享一款基于jQuery头像裁剪插件cropbox,这是一款简单实用的jQuery头像在线裁剪插件.该插件适用于适用浏览器:IE8.360.FireFox.Chrome.Safari.Ope ...
随机推荐
- h5里viewport设置
正确设置: <meta name="viewport" content="width=device-width,initial-scale=1.0"> ...
- Memcache服务器端参数说明
/usr/local/webserver/memcached/bin/memcached -d -m 512 -u root -l 192.168.0.1 -p 11211 -c 512 -P /us ...
- Oracle Redo 以及 Archived日志简述
Oracle通过Redo Archived实现数据的归档 什么是Redo日志 Redo日志记录了数据的变更,用于在数据库出现故障后,进行数据恢复. 功能主要由三个组件实现:Redo Log Buffe ...
- 手打的笔记,java语法中的输入输出,语句,及注释。
手打的笔记: () 内的则为注意事项或者提示 public static void main (String[] args) ******(用一个方法)****{ int i = 10; int j ...
- oracle 中 rownum 和 row_number()
简单的介绍下oracle 中rownum 和 row_number() 使用,实例演示. 参照:http://www.cnblogs.com/zjrstar/archive/2006/08/31/49 ...
- Java——List集合
package om.hanqi.test; import java.util.ArrayList; import java.util.List; public class Test01 { publ ...
- rabbitMQ第四篇:远程调用
前言:前面我们讲解的都是本地服务器,现在如果需要远程计算机上运行一个函数,等待结果.这就是一个不同的故事了,这种模式通常被称为远程过程调用或者RPC. 本章教程我们使用RabbitMQ搭建一个RPC系 ...
- Node学习
参见Node入门 做出node应用的第一个例子 图片上传浏览.
- 因为相同类型的其他实体已具有相同的主键值。在使用 "Attach" 方法或者将实体的状态设置为 "Unchanged" 或 "Modified" 。。。
因为相同类型的其他实体已具有相同的主键值.在使用 "Attach" 方法或者将实体的状态设置为 "Unchanged" 或 "Modified&quo ...
- mysql load data infile的使用 和 SELECT into outfile备份数据库数据
LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name.txt' [REPLACE | IGNORE] INTO TABLE t ...