设为首页 加入收藏

TOP

Cats(2)- Free语法组合,Coproduct-ADT composition(一)
2017-10-10 12:11:55 】 浏览:2045
Tags:Cats Free 语法 组合 Coproduct-ADT composition

    上篇我们介绍了Free类型可以作为一种嵌入式编程语言DSL在函数式编程中对某种特定功能需求进行描述。一个完整的应用可能会涉及多样的关联功能,但如果我们为每个应用都设计一套DSL的话,那么在我们的函数式编程中将会不断重复的功能相似的DSL。我们应该秉承函数式编程的核心思想:函数组合(compositionality)来实现DSL的组合:把DSL拆解成最基础语句ADT,然后用这些ADT来组合成适合应用功能要求的完整DSL。我们还是使用上篇那个Interact DSL,这次再增加一个Login功能:

 1 package demo.app  2 import cats.free.{Free,Inject}  3 object FreeModules {  4   object ADTs {  5     sealed trait Interact[+A]  6     object Interact {  7       case class Ask(prompt: String) extends Interact[String]  8       case class Tell(msg: String) extends Interact[Unit]  9       type FreeInteract[A] = Free[Interact,A] 10       def ask(prompt: String): FreeInteract[String] = Free.liftF(Ask(prompt)) 11       def tell(msg: String): FreeInteract[Unit] = Free.liftF(Tell(msg)) 12  } 13 
14     sealed trait Login[+A] 15     object Login { 16       type FreeLogin[A] = Free[Login,A] 17       case class Authenticate(user: String, pswd: String) extends Login[Boolean] 18       def authenticate(user: String, pswd: String): FreeLogin[Boolean] =
19  Free.liftF(Authenticate(user,pswd)) 20  } 21 
22  } 23 
24 }

上面我们增加了个Login类。我们先来进行DSL编程:

 1   object DSLs {  2  import ADTs._  3  import Interact._  4  import Login._  5     val interactDSL: FreeInteract[Unit]  = for {  6       first <- ask("What's your first name?")  7       last <- ask("What's your last name?")  8       _ <- tell(s"Hello, $first $last!")  9     } yield() 10 
11     val loginDSL: FreeLogin[Boolean] = for { 12       login <- authenticate("Tiger","123") 13     } yield login 14   }

很明显,用一种DSL编程是无法满足Login功能需要的。我们需要像下面这样的DSL:

1  val interactLoginDSL: Free[???,Boolean] = for { 2       uid <- ask("Enter your User ID:") 3       psw <- ask("Enter your Password:") 4       aut <- authenticate(uid,pwd) 5     } yield aut

不过上面的???应该是什么呢?它应该是Interact和Login的集合。cats提供了Coproduct,它是一个树形数据结构:

/** `F` on the left and `G` on the right of [[scala.util.Either]]. * * @param run The underlying [[scala.util.Either]]. */ final case class Coproduct[F[_], G[_], A](run: Either[F[A], G[A]]) {...}

Coproduct 的每一个节点(Either[F[A],G[A]])都是一个ADT,F[A]或者G[A]。我们可以用多层递归Coproduce结构来构建一个多语法的树形结构,如:

1 type H[A] = Coproduct[F,G,A] 2 type I[A] = Coproduct[H,X,A] 3 type J[A] = Coproduct[J,Y,A]  //ADT(F,G,X,Y)

用Coproduct的树形结构可以容纳多种DSL的ADT。在上面的例子里我们需要一个组合的语法InteractLogin:

1 type InteractLogin[A] = Coproduct[Interact,Login,A]

cats提供了Inject类来构建Coproduct:

sealed abstract class Inject[F[_], G[_]] { def inj[A](fa: F[A]): G[A] def prj[A](ga: G[A]): Option[F[A]] } private[free] sealed abstract class InjectInstances { implicit def catsFreeReflexiveInjectInstance[F[_]]: Inject[F, F] =
    new Inject[F, F] { def inj[A](fa: F[A]): F[A] = fa def prj[A](ga: F[A]): Option[F[A]] = Some(ga) } implicit def catsFreeLeftInjectInstance[F[_], G[_]]: Inject[F, Coproduct[F, G, ?]] =
    new Inject[F, Coproduct[F, G, ?]] { def inj[A](fa: F[A]): Coproduct[F, G, A] = Coproduct.leftc(fa) def prj[A](ga: Coproduct[F, G, A]): Option[F[A]] = ga.run.fold(Some(_), _ => None) } implicit def catsFreeRightInjectInstance[F[_], G[_], H[_]](implicit I: Inject[F, G]): Inject[F, Coproduct[H, G, ?]] =
    new Inject[F, Coproduct[H, G, ?]] { def inj[A](fa: F[A]): Coproduct[H, G, A] = Coproduct.rightc(I.inj(fa)) def prj[A](ga: Coproduct[H, G, A]): Option[F[A]] = ga.run.fold(_ => None, I.prj) } }

inj[A](fa: F[A]):G[A]代表将F[A]注入更大的语法集G[A]。cats提供了三种实现了ink函数的Inject隐式实例:

1、catsFreeReflexiveInjectInstance:Inject[F,F]:对单一语法,

首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇scala数据库工具类 下一篇Scala--高阶函数

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目