配色: 字号:
Scalaz- Free :实践-Free In Action - 实用体验
2016-09-24 | 阅:  转:  |  分享 
  
Scalaz-Free:实践-FreeInAction-实用体验
在上面几期讨论中我们连续介绍了FreeMonad。因为FP是纯函数编程,也既是纯函数的组合集成,要求把纯代码和副作用代码可以分离开来。FreeMonad的程序描述(AST)和程序实现(Interpretation)关注分离(separationofconcern)模式恰恰能满足FP要求。我们可以用一些代数数据类型(ADTAlgebraicDataType)来模拟功能,再把这些ADT组合起来形成AST(AbstractSyntaxTree)。AST既是对程序功能的描述,它的组成过程也就是MonadicProgramming了。在另外一个过程中,我们可以按需要去实现各种Interpreter,从而达到实际运算的目的。我认为既然FP也被称为MonadicProgramming,那么FreeMonad应该是FP里最重要的数据结构,它的应用模式代表了主流FP,应该有个规范的具体使用方式。在本次讨论中我们将会集中对FreeMonad的应用模式进行示范体验。

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

1、ADT设计

1sealedtraitKVS[+Next]
2objectKVS{
3caseclassGet[Next](key:String,onValue:String=>Next)extendsKVS[Next]
4caseclassPut[Next](key:String,value:String,n:Next)extendsKVS[Next]
5caseclassDel[Next](key:String,n:Next)extendsKVS[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实例

复制代码
1implicitvalkvsFunctor=newFunctor[KVS]{
2defmap[A,B](kvs:KVS[A])(f:A=>B):KVS[B]=kvsmatch{
3caseGet(key,onResult)=>Get(key,onResultandThenf)
4casePut(key,value,next)=>Put(key,value,f(next))
5caseDel(key,next)=>Del(key,f(next))
6}
7}
复制代码
把A转换成B就是把Free[KVS,A]转成Free[KVS,B],其实就是mapoverNext,对Next进行转换。对于函数C=>Next,map就是函数组合了:(C=>Next)andThen(Next=>B)。

3、类型升格,lifttoFree

1implicitdefkvsToFree[A](ka:KVS[A]):Free[KVS,A]=Free.liftF(ka)
2defput(key:String,value:String):Free[KVS,Unit]=Free.liftF(Put(key,value,()))
3defget(key:String):Free[KVS,String]=Free.liftF(Get(key,identity))
4defdel(key:String):Free[KVS,Unit]=Free.liftF(Del(key,()))
包括隐式类型转换kvsToFree,可以把任何KVS[A]升格成Free[KVS,A]。独立指令升格put,get,del,因为不涉及下一个状态所以使用了()和identity。

4、Composition,FreeMonad组合

1importKVS._
2defmodify(key:String,f:String=>String):Free[KVS,Unit]=
3for{
4v<-Get(key,identity)
5_<-Put(key,f(v),())
6}yield()//>modify:(key:String,f:String=>String)scalaz.Free[Exercises.freeExamples.KVS,Unit]
通过隐式函数kvsToFree把ADTGet,Put升格成Free[KVS,A],然后实现函数组合。

5、功能描述,AST设计

复制代码
1valscript=for{
2_<-put("USA","UnitedStatesOfAmerica")
3_<-put("CHN","China")
4_<-put("PIL","Pilipines")
5_<-put("JPN","Japan")
6_<-modify("CHN",_=>"People''sRepublicOfChina")
7_<-del("PIL")
8chn<-get("CHN")
9}yieldchn//>script:scalaz.Free[Exercises.freeExamples.KVS,String]=Gosub()
复制代码
使用的是独立直接升格指令函数。函数直接返回了Free类型。就像是在for-loop里进行我们熟悉的行令编程:逐条指令编写。

6、功能实现,Interpretation

a、尾递归编译,tail-recursiveinterpretation

复制代码
1deffoldScript(kvs:Free[KVS,String],table:Map[String,String]=Map.empty):Map[String,String]=
2kvs.resume.fold(
3{
4caseGet(key,onResult)=>foldScript(onResult(table(key)),table)
5casePut(key,value,next)=>foldScript(next,table+(key->value))
6caseDel(key,next)=>foldScript(next,table-key)
7},
8_=>table
9)//>foldScript:(kvs:scalaz.Free[Exercises.freeExamples.KVS,String],table:Map[String,String])Map[String,String]
10foldScript(script,Map.empty)//>res0:Map[String,String]=Map(USA->UnitedStatesOfAmerica,CHN->People''sRepublicOfChina,JPN->Japan)
复制代码
注意,fold其实是Either.fold。foldScript是个尾递归函数。这时候Next就成为下一步递归的链接了。

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

复制代码
1typeKVState[A]=State[Map[String,String],A]
2objectKvsToMapextends(KVS~>KVState){
3defapply[A](kvs:KVS[A]):KVState[A]=kvsmatch{
4caseGet(key,onResult)=>State{m=>(m,onResult(m(key)))}
5casePut(key,value,next)=>State{m=>(m+(key->value),next)}
6caseDel(key,next)=>State{m=>(m-key,next)}
7}
8}
9script.foldMap(KvsToMap).run(Map.empty)//>res1:scalaz.Id.Id[(Map[String,String],String)]=(Map(USA->UnitedStatesOfAmerica,CHN->People''sRepublicOfChina,JPN->Japan),People''sRepublicOfChina)
复制代码
c、mutable实现方法:

复制代码
1defgoScript(kvs:Free[KVS,String],table:scala.collection.mutable.Map[String,String]):Unit=
2kvs.go{
3caseGet(key,onResult)=>onResult(table(key))
4casePut(key,value,next)=>table+=(key->value);next
5caseDel(key,next)=>table-=key;next
6}//>goScript:(kvs:scalaz.Free[Exercises.freeExamples.KVS,String],table:scala.collection.mutable.Map[String,String])Unit
7valmutableMap=scala.collection.mutable.Map[String,String]()
8//>mutableMap:scala.collection.mutable.Map[String,String]=Map()
9goScript(script,mutableMap)
10println(mutableMap)//>Map(JPN->Japan,CHN->People''sRepublicOfChina,USA->UnitedStatesOfAmerica)
复制代码
把完整的示范源代码提供给大家:

复制代码
1packageExercises
2importscalaz._
3importScalaz._
4importscala.language.higherKinds
5importscala.language.implicitConversions
6objectfreeExamples{
7sealedtraitKVS[+Next]
8objectKVS{
9caseclassGet[Next](key:String,onValue:String=>Next)extendsKVS[Next]
10caseclassPut[Next](key:String,value:String,n:Next)extendsKVS[Next]
11caseclassDel[Next](key:String,n:Next)extendsKVS[Next]
12implicitvalwww.wang027.comkvsFunctor=newFunctor[KVS]{
13defmap[A,B](kvs:KVS[A])(f:A=>B):KVS[B]=kvsmatch{
14caseGet(key,onResult)=>Get(key,onResultandThenf)
15casePut(key,value,next)=>Put(key,value,f(next))
16caseDel(key,next)=>Del(key,f(next))
17}
18}
19implicitdefkvsToFree[A](ka:KVS[A]):Free[KVS,A]=Free.liftF(ka)
20defput(key:String,value:String):Free[KVS,Unit]=Free.liftF(Put(key,value,()))
21defget(key:String):Free[KVS,String]=Free.liftF(Get(key,identity))
22defdel(key:String):Free[KVS,Unit]=Free.liftF(Del(key,()))
23}
24importKVS._
25defmodify(key:String,f:String=>String):Free[KVS,Unit]=
26for{
27v<-Get(key,identity)
28_<-Put(key,f(v),())
29}yield()
30valscript=for{
31_<-put("USA","UnitedStatesOfAmerica")
32_<-put("CHN","China")
33_<-put("PIL","Pilipines")
34_<-put("JPN","Japan")
35_<-modify("CHN",_=>"People''sRepublicOfChina")
36_<-del("PIL")
37chn<-get("CHN")
38}yieldchn
39
40deffoldScript(kvs:Free[KVS,String],table:Map[String,String]=Map.empty):Map[String,String]=
41kvs.resume.fold(
42{
43caseGet(key,onResult)=>foldScript(onResult(table(key)),table)
44casePut(key,value,next)=>foldScript(next,table+(key->value))
45caseDel(key,next)=>foldScript(next,table-key)
46},
47_=>table
48)
49foldScript(script,Map.empty)
50
51typeKVState[A]=State[Map[String,String],A]
52objectKvsToMapextends(KVS~>KVState){
53defapply[A](kvs:KVS[A]):KVState[A]=kvsmatch{
54caseGet(key,onResult)=>State{m=>(m,onResult(m(key)))}
55casePut(key,value,next)=>State{m=>(m+(key->value),next)}
56caseDel(key,next)=>State{m=>(m-key,next)}
57}
58}
59script.foldMap(KvsToMap).run(Map.empty)
60
61defgoScript(kvs:Free[KVS,String],table:scala.collection.mutable.Map[String,String]):Unit=
62kvs.go{
63caseGet(key,onResult)=>onResult(table(key))
64casePut(key,value,next)=>table+=(key->value);next
65caseDel(key,next)=>table-=key;next
66}
67valmutableMap=scala.collection.mutable.Map[String,String]()
68goScript(script,mutableMap)
69println(mutableMap)
70}
献花(0)
+1
(本文系thedust79首藏)