设为首页 加入收藏

TOP

Cats(3)- freeK-Free编程更轻松,Free programming with freeK(一)
2017-10-10 12:11:53 】 浏览:6685
Tags:Cats freeK-Free 编程 轻松 Free programming with freeK

   在上一节我们讨论了通过Coproduct来实现DSL组合:用一些功能简单的基础DSL组合成符合大型多复杂功能应用的DSL。但是我们发现:cats在处理多层递归Coproduct结构时会出现编译问题。再就是Free编程是一个繁复的工作,容易出错,造成编程效率的低下。由于Free编程目前是函数式编程的主要方式(我个人认为),我们必须克服Free编程的效率问题。通过尝试,发现freeK可以作为一个很好的Free编程工具。freeK是个开源的泛函组件库,我们会在这次讨论里用freeK来完成上次讨论中以失败暂停的多层Coproduct Free程序。我们先试试Interact和Login两个混合DSL例子:

 1   object ADTs {  2     sealed trait Interact[+A]  3     object Interact {  4       case class Ask(prompt: String) extends Interact[String]  5       case class Tell(msg: String) extends Interact[Unit]  6  }  7     sealed trait Login[+A]  8     object Login {  9       case class Authenticate(uid: String, pwd: String) extends Login[Boolean] 10  } 11  } 12   object DSLs { 13  import ADTs._ 14  import Interact._ 15  import Login._ 16     type PRG = Interact :|: Login :|: NilDSL 17     val PRG = DSL.Make[PRG] 18 
19     val authenticDSL: Free[PRG.Cop, Boolean] =
20       for { 21         uid <- Ask("Enter your user id:").freek[PRG] 22         pwd <- Ask("Enter password:").freek[PRG] 23         auth <- Authenticate(uid,pwd).freek[PRG] 24       } yield auth 25   }

从ADT到DSL设计,用freeK使代码简单了很多。我们不需要再对ADT进行Inject和Free.liftF升格了,但必须在没条语句后附加.freek[PRG]。本来可以通过隐式转换来避免这样的重复代码,但scalac会在编译时产生一些怪异现象。这个PRG就是freeK的Coproduct结构管理方法,PRG.Cop就是当前的Coproduct。freeK是用:|:符号来连接DSL的,替代了我们之前繁复的Inject操作。

功能实现方面有什么变化吗?

 1   object IMPLs {  2  import ADTs._  3  import Interact._  4  import Login._  5     val idInteract = new (Interact ~> Id) {  6       def apply[A](ia: Interact[A]): Id[A] = ia match {  7         case Ask(p) => {println(p); scala.io.StdIn.readLine}  8         case Tell(m) => println(m)  9  } 10  } 11     val idLogin = new (Login ~> Id) { 12       def apply[A](la: Login[A]): Id[A] = la match { 13         case Authenticate(u,p) => (u,p) match { 14           case ("Tiger","123") => true
15           case _ => false
16  } 17  } 18  } 19     val interactLogin = idInteract :&: idLogin 20   }

这部分没有什么变化。freeK用:&:符号替换了or操作符。

那我们又该如何运行用freeK编制的程序呢?

 

1 object freeKDemo extends App { 2  import FreeKModules._ 3  import DSLs._ 4  import IMPLs._ 5   val r0 = authenticDSL.foldMap(interactLogin.nat) 6   val r = authenticDSL.interpret(interactLogin) 7  println(r0) 8  println(r) 9 }

 interactLogin.nat就是以前的G[A]~>Id,所以我们依然可以用cats提供的foldMap来运算。不过freeK提供了更先进的interpret函数。它的特点是不要求Coproduct结构的构建顺序,我们无须再特别注意用inject构建Coproduct时的先后顺序了。也就是说:|:和:&:符号的左右元素可以不分,这将大大提高编程效率。

我们还是按上次的功能设计用Reader来进行用户密码验证功能的依赖注入。依赖界面定义如下:

1 object Dependencies { 2  trait PasswordControl { 3  val mapPasswords: Map[String,String] 4  def matchUserPassword(uid: String, pwd: String): Boolean 5  } 6 }

我们需要把Interact和Login都对应到Reader:

 1  import Dependencies._  2     type ReaderContext[A] = Reader[PasswordControl,A]  3     object readerInteract extends (Interact ~> ReaderContext) {  4       def apply[A](ia: Interact[A]): ReaderContext[A] = ia match {  5         case Ask(p) => Reader {pc => {println(p); scala.io.StdIn.readLine}}  6         case Tell(m) => Reader {_ => println(m)}  7  }  8  }  9     object readerLogin extends (Login ~> ReaderContext) { 10       def apply[A](la: Login[A]): ReaderContext[A] = la match { 11         case Authenticate(u,p) => Reader {pc => pc.matchUserPassword(u,p)} 12  } 13  } 14     val userInteractLogin = readerLogin :&: readerInteract

注意在上面我故意调换了:&:符号两边对象来证明interpret函数是不依赖Coproduct顺序的。

运算时我们需要构建一个测试的PasswordControl实例,然后把它传入Reader.run函数:

 1 object freeKDemo extends App {  2  import FreeKModules._  3  import DSLs._  4  import IMPLs._  5  // val r0 = authenticDSL.foldMap(
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scala--高阶函数 下一篇scala学习手记36 - 容器基础

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目