Cats(1)- 从Free开始,Free cats

by admin on 2018年11月16日
1      implicit object interactFunctor extends Functor[Interact]  {
2         def map[A,B](ia: Interact[A])(f: A => B): Interact[B] = ia match {
3            case Ask(p) => ???
4            case Tell(m) => ???
5         }
6       }

反而以地上走的动物害怕自己暴露自己之职,所以啊就是摘当草丛树丛中宁静地躲起来。包括诸如狮子和老虎这样的头号掠食者在捕捉猎物的时刻不但不见面发声,还见面努力的削减捕猎过程被的噪音,避免干扰目标猎物。

 

这就是说,原始人是什么用音乐及舞蹈保护自己之啊?

 

这就是说,歌唱的源于又是什么啊?

 
cats是scala的一个初的函数式编程工具库,其计划原理基本持续了scalaz:大家都是haskell
typeclass的scala版实现。当然,cats在scalaz的底子及起落实细节、库组织结构以及调用方式达成拓展了部分优化,所以对用户来说:cats的根基数据类型、数据结构在效能及跟scalaz是大致相同的,可能来一对语法上的更动。与scalaz著名抽象、复杂的语法表现形式相比,cats的语法可能再像、简单直白。在scalaz的学过程遭到,我们询问及所谓函数式编程就是monadic
Programming:即用monad这样的数据类型来构建程序。而事实上可行之monadic
programming就是因此Free-Monad编程了。因为Free-Monad程序是真可运行的,或者说是可以实现安康运转的,因为它们可以管在一定的堆积栈内实现无限运算。我们理解:函数式编程模式之运作方式为递归算法为主,flatMap函数本身便是千篇一律种递归算法。这即预示着monadic
programming很易导致堆栈溢出问题(StackOverflowError)。当我们管日常的泛函类型F[A]升级成Free-Monad后就是会充分利用Free-Monad安全运算能力来构建实际而运行的主次了。由于我们于头里都详尽的询问了scalaz的大部typeclass,包括Free,对cats的讨论就由Free开始,聚焦在cats.Free编程模式方面。同时,我们得于采用cats.Free的历程中针对cats的任何数据类型进行补充了解。

当,这些只能算是粗略的统揽,但是咱为大抵能承认,音乐起源于人类头在之急需。只有满足了活就同极其核心的基准,才产生或以繁衍后代、娱乐、哲学思维提上日程。

S是个高阶类,就是如出一辙种植函数式运算。值得注意的是:现在S不欲是独Functor了。因为Free的一个实例Suspend类型是这般的:

就人类是当地方生活要同时闹腾不安静的种,这个特质难道会被人类在得再安全?

cats.Free的色款式如下:

优先来看看人类的跑动能力:就算当时在非洲大陆上的故人们都有三至奥运金牌得主“闪电侠”博尔特的速度,那吧可大凡38公里/小时,而猎豹的参天时速是113公里/小时,羚羊的危时速80公里/小时至96公里/小时。所以于这底条件,不管是追逐猎物,还是逃出凶猛野兽的逮捕,奔跑实在不可知算作是人类的优势。

1 import ADTs._,DSLs._,IMPLs._
2    val testData = Map("What's your first name?" -> "Tiger",
3   "What's your last name?" -> "Chan")    //> testData  : scala.collection.immutable.Map[String,String] = Map(What's your first name? -> Tiger, What's your last name? -> Chan)
4   val prgRunner = prg.foldMap(tester)    //> prgRunner  : demo.ws.catsFree.IMPLs.Tester[Unit] = <function1>
5   prgRunner(testData)                    //> res0: (List[demo.ws.catsFree.IMPLs.Message], Unit) = (List(Hello Tiger Chan),())

有人说音乐起源于原始人类追求异性的行为,为了还好之繁衍后代,原始人学会用音乐来交流情感。好像听上有些道理,因为确实发生一些音乐会于人性感的感觉到。

 1       sealed trait FunInteract[NS]
 2       object FunInteract {
 3         case class FunAsk[NS](prompt: String, onInput: String =>  NS) extends FunInteract[NS]
 4         case class FunTell[NS](msg: String, ns: NS) extends FunInteract[NS]
 5         
 6         def funAsk(prompt: String): Free[FunInteract,String] = Free.liftF(FunAsk(prompt,identity))
 7         def funAskInt(prompt: String): Free[FunInteract,Int] = Free.liftF(FunAsk(prompt,_.toInt))
 8         def funTell(msg: String): Free[FunInteract,Unit] = Free.liftF(FunTell(msg,()))
 9         
10         implicit object funInteract extends Functor[FunInteract] {
11            def map[A,NS](fa: FunInteract[A])(f: A => NS) = fa match {
12               case FunAsk(prompt,input) => FunAsk(prompt,input andThen f)
13               case FunTell(msg,ns) => FunTell(msg,f(ns))
14            }
15         }
16       }

音乐的最初形态是赞叹不已,因为其不待借助乐器。那么音乐之来源可以说不怕是赞赏的根源。

本我们可为此这个DSL来编排KVS程序了: 

然,按照逻辑来说要先来音乐之家伙,才会帮忙巫师更好之进入状态。所以可能是预先来乐,后有的巫术和祭祀礼仪

 

1、匪议音合唱,当族群成员演唱和声时,合唱声响增强,泛音当不同音高互相碰撞,产生而震动的与声声响,结果是越来越奋发、音量更要命的声音。更要紧之是稍稍群体通过不商音合唱可以针对掠食者造成“这里产生同一坏群人”的部落印象,明白之掠食者自然会被动。

 

难道说原始人谈恋爱还要组队为?

/**
   * Suspend a value within a functor lifting it to a Free.
   */
  def liftF[F[_], A](value: F[A]): Free[F, A] = Suspend(value)

还有,我们省原始人的铁——牙齿:众本地生活之动物都管牙当做了猎食和防守之基本点武器,而内部最全杀器是犬齿。然而500万年前人类的犬齿就开始走下坡路成今天之规范了,大家可比一下底的有限张照片。很多丁当犬齿的退化是因人类发现并熟练的掌握了生气,因为吃熟食所以退化了,可是以目前调查的结果,人类首用火是以200万年前,那么500万年前人类的犬齿已经落伍该如何分解啊?这只能证明犬齿对于500万年前之人类生存都远非多好的意图了。

 1  object DSLs {
 2     import ADTs._
 3     import KVStoreA._
 4     def prg: KVStore[Option[Int]] =
 5     for {
 6       _ <- put[Int]("wild-cats", 2)
 7       _ <- mod[Int]("wild-cats", (_ + 12))
 8       _ <- put[Int]("tame-cats", 5)
 9       n <- get[Int]("wild-cats")
10       _ <- del("tame-cats")
11     } yield n
12   }

热带雨林中的禽和树上的猴是者星球上极其吵杂的动物,而陆地动物(非洲野牛、斑马)通常还老平静,甚至使潜伏起来。为什么会来这样的差异为?那是因树上物种与地上物种的看守政策不同。

今及时简单独ADT是起型参数NS的了:FunAsk[NS],FunTell[NS]。NS代表了ADT当前色,如FunAsk[Int]、FunTell[String]…,现在这简单个ADT都由此项目参数NS变成了但map的靶子了,如FunAsk[String]
>>> FunAsk[String], FunAsk[String] >>>
FunAsk[Int]…。所以我们可以生顺利的落实object
funInteract的map函数。但是,一个好玩的情景是:为了落实这种状态转换,如果ADT需要返回操作结果,就得具备一个率领状态转换的体制,如FunAsk类型里之onInput:
String =>
NS:它代表funAsk函数返回的结果可以对下一个态。新增函数funAskInt是单深好的以身作则:通过返回的String结果以状态转换到FunAsk[Int]状态。函数funTell不回结果,所以FunTell没有状态转换机制。scalaz旧版本Free.Suspend的花色款式是:Suspend[F[Free,A]],这是一个递归类型,内部的Free代表下一个状态。由于我们亟须用F.map才能够取出下一个状态,所以F必须是个Functor。我们应有注意到如果ADT是Functor的言语会招Free程序的冗余代码。既然cats.Free对F[A]从未有过安装Functor门槛,那么我们应尽量避免使用Functor。

2、音域,人类男性和坤的音域相差一个八度,但男性低沉的动静对恫吓敌人好有效,而且音域较逊色之音吗能够传播到又远之距离。当人类男性的野蛮吼叫集中起来的时节,会受附近那些企图袭击的野兽造成恐惧的感觉到,从而吓跑他们。

 1     type KVStore[A] = Free[KVStoreA,A]
 2     object KVStoreA {
 3       def put[T](key: String, value: T): KVStore[Unit] =
 4         Free.liftF[KVStoreA,Unit](Put[T](key,value))
 5       def get[T](key: String): KVStore[Option[T]] =
 6         Free.liftF[KVStoreA,Option[T]](Get[T](key))
 7       def del(key: String): KVStore[Unit] =
 8         Free.liftF[KVStoreA,Unit](Del(key))
 9       def mod[T](key: String, f: T => T): KVStore[Unit] =
10         for {
11           opt <- get[T](key)
12           _ <- opt.map {t => put[T](key,f(t))}.getOrElse(Free.pure(()))
13         } yield()
14     }

可是如果是看法成立,那么人类的大合唱行为又欠怎么讲啊?

1  implicit val testWriterRecT = new RecursiveTailRecM[WriterTester]{}
2            //> testWriterRecT  : cats.RecursiveTailRecM[demo.ws.catsFree.IMPLs.WriterTester] = demo.ws.catsFree$$anonfun$main$1$$anon$2@6093dd95
3   val prgRunner = prg.foldMap(testWriter)         //> prgRunner  : demo.ws.catsFree.IMPLs.WriterTester[Unit] = WriterT(<function1>)
4   prgRunner.run(testData)._1.map(println)         //> Hello Tiger Chan
5                                                   //| res0: List[Unit] = List(())

那我们接下去看看,原始人类和外动物比,有没出过人之处呢:

 我们于scalaz.Free的座谈中连不曾能够详细地分析在什么动静下S[_]非得是只Functor。下面我们用因此有篇幅来分析。

再次来探视身体能力:可能没人会面怀疑身体能力和防御能力之间的干。虽然于人类社会里产生那么多扣起身材高大肌肉发达的“猛男”,但不幸的凡人的力不但会小于与我们体型相当的动物,甚至块头较人类小森底动物(例如黑猩猩)的能力还要比较健壮的选手大好几倍。1924年就有过这样的报导:一条纽约布朗克斯动物园的公黑猩猩,都能够坦然地于张力测力器上拉出385千克的惊人成绩(一般同样体重的总人口只能拉96千范围,迄今尚未人类会打破这记录),也难怪《人猿星球》里的猩猩能拿食指瞬间拍倒。所以人类自然吧不是为力量战胜之。

 

这就是说人类的称道和舞蹈为什么能够成功之吓退掠食者,原因发生三:

map的来意是故一个函数A
=>
B把F[A]转成F[B]。也便是拿讲话状态从F[A]转成F[B],但每当Interact的景象里F[B]都是明确的Interact[Unit]和Interact[String]些微种植状态,而map的f是A
=>
B,在上头的以身作则里我们欠怎么施用f来取得这Interact[B]为?从点的言传身教里我们着眼可以汲取Ask和Tell这半单ADT纯粹是以模仿ask和tell这有限只函数。ask和tell分别返回Free版本的String,Unit结果。可以说:Interact并从未更换到下一个状态的渴求。那么一旦我们将ADT调整成为下面这样吧:

再有人口当,音乐来与巫术和原来之祝福仪式。

Free程序的表征是算式(description)/算法(implementation)关注分离(separation
of concern):我们所以同样组数据类型来模拟一种编程语句ADT(algebraic data
type),这同一组ADT就形成了同一种植定制的编程语言DSL(domain specific
language)。Free的编程部分即使是故DSL来描述程序功能(description of
purpose),即算式了。算法就用DSL描述的法力的切实落实,可以产生多种的效用实现方式。我们先行看个大概的DSL:

我们的祖先把大声赞叹、喊让作为对抗掠食者防御系统的中心奥义。集体大声、有板的歌唱跟喝,伴随在刚刚猛、威胁性的人活动(这恐怕是舞蹈的初期形态)来对抗并吓退掠食者。下面这幅图是欧洲画家对原始部落舞蹈场景的记录,可以看来跳舞的都是群体里的常年男性。

 

比方解开音乐之来源的谜,我们定要是返回人类进化之源流——大自然中失去。就深受咱们先看大自然中之动物对叫声的国策是哪些的:

 

树上的动物便根据自身之份量来控制栖所的万丈,体重更加爱,住得越来越强,就相对更安全。因为尤其强意味着树枝越细致,体型比生之掠食者通常无法抵达这样的冲天。这便是怎会爬树的50公斤的猎豹很不便捕捉10公斤的灵长类动物之缘由。因此树上的动物便以称颂等声音信号的点子来交流,尽管声音能给掠食者瞬间识别出她所在的位置,但为只好当培育生干瞪眼。下图的猎豹便从来不会吸引这只是猴子。

sealed abstract class Free[S[_], A] extends Product with Serializable {...}

3、板,节奏性音乐舞蹈可以转拿兵携带到集体肯定交战恍惚状态,这象征官优先给个人的模式给打开,也一律代表战士们的勇敢无畏模式被启动。当原始人族群里之战士等再次发出勇气时,面对掠食者和其他敌人时本会愈发从容、大胆,往往也能还易击退敌人。同时,这也是怎么看摇滚现场演出、音乐会的上,人会倍感更是激动的缘由,因为若会当温馨和现场的观众融为一体,个体不以重大了。

/**
   * Catamorphism for `Free`.
   *
   * Run to completion, mapping the suspension with the given
   * transformation at each step and accumulating into the monad `M`.
   *
   * This method uses `tailRecM` to provide stack-safety.
   */
  final def foldMap[M[_]](f: FunctionK[S, M])(implicit M: Monad[M], r: RecursiveTailRecM[M]): M[A] =
    r.sameType(M).tailRecM(this)(_.step match {
      case Pure(a) => M.pure(Right(a))
      case Suspend(sa) => M.map(f(sa))(Right(_))
      case FlatMapped(c, g) => M.map(c.foldMap(f))(cc => Left(g(cc)))
    })

以今有原始部落里,当巫师或萨满在举行仪式时,依然亟待依靠有韵律的吟唱、跳舞、达到通灵的状态,也就是是俗话说的跳大神。

 1    import cats.data.WriterT
 2     type WF[A] = Map[Prompt,Reply] => A
 3     type WriterTester[A] = WriterT[WF,List[Message],A]
 4     def testerToWriter[A](f: Map[Prompt,Reply] => (List[Message],A)) =
 5     WriterT[WF,List[Message],A](f)
 6     object testWriter extends (Interact ~> WriterTester) {
 7       import Interact._
 8       def apply[A](ia: Interact[A]): WriterTester[A] = ia match {
 9         case Ask(p) => testerToWriter(m => (List(),m(p)))
10         case Tell(m) => testerToWriter(_ => (List(m),()))
11       }
12     }

这就是说音乐之源于到底以哪吧?(肯定不是根源于动画片《摩登原始人》)

 

关于音乐的发源,真的是过多说纷纭。

我们再次来探在cats里是什么样运算Free
DSL程序的。相对scalaz而言,cats的运算函数简单的几近,就一个foldMap,我们来探视它的定义:

最后,我们还来探自然的警备——皮肤:甭管捕食者还是猎物,大部分种都见面时有发生丰厚坚韧的皮毛,即使是圈起并凭特别之处的羚羊皮毛都远厚了我们人类那柔软脆弱的皮层。虽然腹肌还是人类的难堪,但光无毛略显脆弱,而肤浅的存则是为防备猎食者和敌人的尖牙利齿伤害到温馨的肌、内脏。所以人类在前行历程被呢放弃了上下一心的老虎皮。

1   import cats.{Monad,RecursiveTailRecM}
2   implicitly[Monad[KVStoreState]]      //> res1: cats.Monad[demo.ws.catsFreeKVS.IMPLs.KVStoreState] = cats.data.StateT Instances$$anon$2@71bbf57e
3   implicitly[RecursiveTailRecM[KVStoreState]]     //> res2: cats.RecursiveTailRecM[demo.ws.catsFreeKVS.IMPLs.KVStoreState] = cats.RecursiveTailRecM$$anon$1@7f13d6e

如此看之言语,原始人类基本上是大自然中尽弱鸡的种了,这些生理及的数额向无克支持起会给好安全在的防守政策。人类不仅未安静躲藏,还要当本土不停止的发出声音,这是不是意味着人类早该灭绝?

那一旦运算testWriter呢?我们事先抱WriterT的Monad实例: 

结果是,我们或以及时杀机四东躲西藏的自然界中在到了当今。想象一下,在古的粗野大地上,我们的先人最讨厌的量即使是黑夜的亲临。当还不曾生气起之前,我们的上代如何抗掠食者的侵呢?所以发生同样种植可能大高之讲——音乐之源于并非吸引异性,也不是为进入与神沟通的状态,而是以掩护好。

得发对ADT类型要求结论后,我们就示范cats的Free编程。下面是Free程序的力量实现interpret部分(implementation):

 1     import cats.Monad
 2     implicit val testerMonad = new Monad[Tester] with RecursiveTailRecM[Tester]{
 3       override def pure[A](a: A): Tester[A] = _ => (List(),a)
 4       override def flatMap[A,B](ta: Tester[A])(f: A => Tester[B]): Tester[B] = m => {
 5         val (o1,a1) = ta(m)
 6         val (o2,a2) = f(a1)(m)
 7         (o1 ++ o2, a2)
 8       }
 9       override def tailRecM[A,B](a: A)(f: A => Tester[Either[A,B]]): Tester[B] =
10         defaultTailRecM(a)(f)
11     }

除要求M是单Monad之外,cats还要求M的RecursiveTailRecM隐式实例。那么什么是RecursiveTailRecM呢:

 

 1 import cats.free._
 2 import cats.instances.all._
 3 object catsFreeKVS {
 4   object ADTs {
 5     sealed trait KVStoreA[+A]
 6     case class Put[T](key: String, value: T) extends KVStoreA[Unit]
 7     case class Get[T](key: String) extends KVStoreA[Option[T]]
 8     case class Del(key: String) extends KVStoreA[Unit]
 9     type KVStore[A] = Free[KVStoreA,A]
10     object KVStoreA {
11       def put[T](key: String, value: T): KVStore[Unit] =
12         Free.liftF[KVStoreA,Unit](Put[T](key,value))
13       def get[T](key: String): KVStore[Option[T]] =
14         Free.liftF[KVStoreA,Option[T]](Get[T](key))
15       def del(key: String): KVStore[Unit] =
16         Free.liftF[KVStoreA,Unit](Del(key))
17       def mod[T](key: String, f: T => T): KVStore[Unit] =
18         for {
19           opt <- get[T](key)
20           _ <- opt.map {t => put[T](key,f(t))}.getOrElse(Free.pure(()))
21         } yield()
22     }
23   }
24   object DSLs {
25     import ADTs._
26     import KVStoreA._
27     def prg: KVStore[Option[Int]] =
28     for {
29       _ <- put[Int]("wild-cats", 2)
30       _ <- mod[Int]("wild-cats", (_ + 12))
31       _ <- put[Int]("tame-cats", 5)
32       n <- get[Int]("wild-cats")
33       _ <- del("tame-cats")
34     } yield n
35   }
36   object IMPLs {
37     import ADTs._
38     import cats.{~>}
39     import cats.data.State
40    
41     type KVStoreState[A] = State[Map[String, Any], A]
42     val kvsToState: KVStoreA ~> KVStoreState = new (KVStoreA ~> KVStoreState) {
43       def apply[A](fa: KVStoreA[A]): KVStoreState[A] =
44         fa match {
45           case Put(key, value) => State { (s:Map[String, Any]) =>
46              (s.updated(key, value),()) }
47           case Get(key) => State { (s:Map[String, Any]) =>
48             (s,s.get(key).asInstanceOf[A]) }
49           case Del(key) => State { (s:Map[String, Any]) =>
50               (s - key, (())) }
51         }
52     }
53   }
54   import ADTs._,DSLs._,IMPLs._
55   val prgRunner = prg.foldMap(kvsToState)
56   prgRunner.run(Map.empty).value
57   
58   import cats.{Monad,RecursiveTailRecM}
59   implicitly[Monad[KVStoreState]]
60   implicitly[RecursiveTailRecM[KVStoreState]]
61 }

咱们就此RecursiveTailRecM来确保这Monad类型与tailRecM是相当的,这是平等种植运算安全法,所以于foldMap函数里r.sameType(M).tailRecM保证了tailRecM不见面招致StackOverflowError。cats.Free里还出一致种植不需种安全视察的函数foldMapUnsafe:

 

然后构建一个RecursiveTailRecM实例后再用相同的测试数据来运算:

 

以斯例子里Interact并无是一个Functor,因为咱们鞭长莫及取Interact
Functor实例的map函数。先叫我们分析一下Functor的map:

运算结果一律。

 

假如我们因此Writer来实现Interact,实际上就是是管Ask和Tell都提升成Writer类型。

 1 import cats.free._
 2 import cats.Functor
 3 object catsFree {
 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       
10       def ask(prompt: String): Free[Interact,String] = Free.liftF(Ask(prompt))
11       def tell(msg: String): Free[Interact,Unit] = Free.liftF(Tell(msg))
12 
13 
14       implicit object interactFunctor extends Functor[Interact]  {
15         def map[A,B](ia: Interact[A])(f: A => B): Interact[B] = ???
16       /*   ia match {
17            case Ask(p) => ???
18            case Tell(m) => ???
19         } */
20       }  
21     }
22   }
23   object DSLs {
24     import ADTs._
25     import Interact._
26     val prg: Free[Interact,Unit] = for {
27       first <- ask("What's your first name?")
28       last <- ask("What's your last name?")
29       _ <- tell(s"Hello $first $last")
30     } yield()
31   }

 

 

 1     type Prompt = String
 2     type Reply = String
 3     type Message = String
 4     type Tester[A] = Map[Prompt,Reply] => (List[Message],A)
 5     object tester extends (Interact ~> Tester) {
 6       def apply[A](ia: Interact[A]): Tester[A] = ia match {
 7         case Ask(p) => { m => (List(), m(p)) }
 8         case Tell(m) => { _ => (List(m), ()) }
 9       }
10     }
11     import cats.Monad
12     implicit val testerMonad = new Monad[Tester] {
13       override def pure[A](a: A): Tester[A] = _ => (List(),a)
14       override def flatMap[A,B](ta: Tester[A])(f: A => Tester[B]): Tester[B] = m => {
15         val (o1,a1) = ta(m)
16         val (o2,a2) = f(a1)(m)
17         (o1 ++ o2, a2)
18       }
19       override def tailRecM[A,B](a: A)(f: A => Tester[Either[A,B]]): Tester[B] =
20          defaultTailRecM(a)(f)
21     }
22   }

 

KVStore:

private[data] sealed trait StateTInstances2 {
  implicit def catsDataMonadForStateT[F[_], S](implicit F0: Monad[F]): Monad[StateT[F, S, ?]] =
    new StateTMonad[F, S] { implicit def F = F0 }

  implicit def catsDataRecursiveTailRecMForStateT[F[_]: RecursiveTailRecM, S]: RecursiveTailRecM[StateT[F, S, ?]] = RecursiveTailRecM.create[StateT[F, S, ?]]

  implicit def catsDataSemigroupKForStateT[F[_], S](implicit F0: Monad[F], G0: SemigroupK[F]): SemigroupK[StateT[F, S, ?]] =
    new StateTSemigroupK[F, S] { implicit def F = F0; implicit def G = G0 }
}

对应之仿功能函数设计如下:

以cats的StateT.scala里好搜索到即段代码:

1     import ADTs._
2     object iconsole extends (Interact ~> Id) {
3       def apply[A](ia: Interact[A]): Id[A] = ia match {
4          case Ask(p) => {println(p); readLine}
5          case Tell(m) => println(m)
6       }
7     }
8   }

 

咱把KVStoreA
ADT模拟成对State结构的S转换(mutation),返回State{S=>(S,A)}。KVStoreState[A]类型的S参数为immutable.Map[String,
Any],所以我们在S转换操作时用immutable
map的操作函数来构建新的map返回,典型的pure
code。我们来运算一下KVStoreA程序:

 

1   object ADTs {
2     sealed trait KVStoreA[+A]
3     case class Put[T](key: String, value: T) extends KVStoreA[Unit]
4     case class Get[T](key: String) extends KVStoreA[Option[T]]
5     case class Del(key: String) extends KVStoreA[Unit]
6   }

我们又示范一下cats官方文件里关于free
monad例子:模拟一个KVStore的put,get,delete功能。ADT设计如下:

1    implicit val testWriterMonad =  WriterT.catsDataMonadWriterForWriterT[WF,List[Message]]
  1 import cats.free._
  2 import cats.{Functor, RecursiveTailRecM}
  3 object catsFree {
  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 
 10       def ask(prompt: String): Free[Interact,String] = Free.liftF(Ask(prompt))
 11       def tell(msg: String): Free[Interact,Unit] = Free.liftF(Tell(msg))
 12 
 13 
 14       implicit object interactFunctor extends Functor[Interact]  {
 15         def map[A,B](ia: Interact[A])(f: A => B): Interact[B] = ???
 16         /*   ia match {
 17              case Ask(p) => ???
 18              case Tell(m) => ???
 19           } */
 20       }
 21 
 22       sealed trait FunInteract[NS]
 23       object FunInteract {
 24         case class FunAsk[NS](prompt: String, onInput: String =>  NS) extends FunInteract[NS]
 25         case class FunTell[NS](msg: String, ns: NS) extends FunInteract[NS]
 26 
 27         def funAsk(prompt: String): Free[FunInteract,String] = Free.liftF(FunAsk(prompt,identity))
 28         def funAskInt(prompt: String): Free[FunInteract,Int] = Free.liftF(FunAsk(prompt,_.toInt))
 29         def funTell(msg: String): Free[FunInteract,Unit] = Free.liftF(FunTell(msg,()))
 30 
 31         implicit object funInteract extends Functor[FunInteract] {
 32           def map[A,NS](fa: FunInteract[A])(f: A => NS) = fa match {
 33             case FunAsk(prompt,input) => FunAsk(prompt,input andThen f)
 34             case FunTell(msg,ns) => FunTell(msg,f(ns))
 35           }
 36         }
 37       }
 38     }
 39   }
 40   object DSLs {
 41     import ADTs._
 42     import Interact._
 43     val prg: Free[Interact,Unit] = for {
 44       first <- ask("What's your first name?")
 45       last <- ask("What's your last name?")
 46       _ <- tell(s"Hello $first $last")
 47     } yield()
 48   }
 49   object IMPLs {
 50     import cats.{Id,~>}
 51     import ADTs._
 52     import Interact._
 53     object iconsole extends (Interact ~> Id) {
 54       def apply[A](ia: Interact[A]): Id[A] = ia match {
 55         case Ask(p) => {println(p); readLine}
 56         case Tell(m) => println(m)
 57       }
 58     }
 59 
 60     type Prompt = String
 61     type Reply = String
 62     type Message = String
 63     type Tester[A] = Map[Prompt,Reply] => (List[Message],A)
 64     object tester extends (Interact ~> Tester) {
 65       def apply[A](ia: Interact[A]): Tester[A] = ia match {
 66         case Ask(p) => { m => (List(), m(p)) }
 67         case Tell(m) => { _ => (List(m), ()) }
 68       }
 69     }
 70     import cats.Monad
 71     implicit val testerMonad = new Monad[Tester] with RecursiveTailRecM[Tester]{
 72       override def pure[A](a: A): Tester[A] = _ => (List(),a)
 73       override def flatMap[A,B](ta: Tester[A])(f: A => Tester[B]): Tester[B] = m => {
 74         val (o1,a1) = ta(m)
 75         val (o2,a2) = f(a1)(m)
 76         (o1 ++ o2, a2)
 77       }
 78       override def tailRecM[A,B](a: A)(f: A => Tester[Either[A,B]]): Tester[B] =
 79         defaultTailRecM(a)(f)
 80     }
 81     import cats.data.WriterT
 82     import cats.instances.all._
 83     type WF[A] = Map[Prompt,Reply] => A
 84     type WriterTester[A] = WriterT[WF,List[Message],A]
 85     def testerToWriter[A](f: Map[Prompt,Reply] => (List[Message],A)) =
 86       WriterT[WF,List[Message],A](f)
 87     implicit val testWriterMonad =  WriterT.catsDataMonadWriterForWriterT[WF,List[Message]]
 88     object testWriter extends (Interact ~> WriterTester) {
 89       import Interact._
 90       def apply[A](ia: Interact[A]): WriterTester[A] = ia match {
 91         case Ask(p) => testerToWriter(m => (List(),m(p)))
 92         case Tell(m) => testerToWriter(_ => (List(m),()))
 93       }
 94     }
 95   }
 96 
 97   import ADTs._,DSLs._,IMPLs._
 98    val testData = Map("What's your first name?" -> "Tiger",
 99   "What's your last name?" -> "Chan")
100   //val prgRunner = prg.foldMap(tester)
101   //prgRunner(testData)
102   implicit val testWriterRecT = new RecursiveTailRecM[WriterTester]{}
103   val prgRunner = prg.foldMap(testWriter)
104   prgRunner.run(testData)._1.map(println)
105 }

 

在意一下mod函数:它是由于基础函数get和put组合而成。我们渴求拥有以for内的门类为Free[KVStoreA,?],所以当f函数施用后只要opt变成None时虽返回结果Free.pure(()),它的色是:Free[Nothing,Unit],Nothing是KVStoreA的子类。

但是难道不欲Monad、RecursiveTailRecM实例了邪?实际上cats已经提供了State的Monad和RecursiveTailRecM实例:

DSL程序的意义实现即是管ADT
F[A]对诺交实际的授命集G[A],在Free编程里之所以NaturalTransformation
~>来兑现。注意G[A]必须是只Monad。在上头的例证里对承诺提到是:Interact~>Id,代表直接对诺到运算指令println和readLine。我们为可兑现其他一个本: 

 

/**
 * This is a marker type that promises that the method
 * .tailRecM for this type is stack-safe for arbitrary recursion.
 */
trait RecursiveTailRecM[F[_]] extends Serializable {
  /*
   * you can call RecursiveTailRecM[F].sameType(Monad[F]).tailRec
   * to have a static check that the types agree
   * for safer usage of tailRecM
   */
  final def sameType[M[_[_]]](m: M[F]): M[F] = m
}

我们可以经过State数据结纯代码(pure
code)方式来贯彻用immutable map的KVStore:

 

夫函数不需要RecursiveTailRecM。下面我们挑选能确保运算安全之法子来运算tester:首先我们用Tester类型的Monad和RecursiveTailRecM实例:

自己把上面两只示范的源代码提供在下面:

 

咱无待map就得把F[A]升格成Free

1   import ADTs._,DSLs._,IMPLs._
2   val prgRunner = prg.foldMap(kvsToState)    //> prgRunner  : demo.ws.catsFreeKVS.IMPLs.KVStoreState[Option[Int]] = cats.data.StateT@2cfb4a64
3   prgRunner.run(Map.empty).value       //> res0: (Map[String,Any], Option[Int]) = (Map(wild-cats -> 14),Some(14))
/** Suspend the computation with the given suspension. */
  private final case class Suspend[S[_], A](a: S[A]) extends Free[S, A]

 

 1  object IMPLs {
 2     import ADTs._
 3     import cats.{~>}
 4     import cats.data.State
 5    
 6     type KVStoreState[A] = State[Map[String, Any], A]
 7     val kvsToState: KVStoreA ~> KVStoreState = new (KVStoreA ~> KVStoreState) {
 8       def apply[A](fa: KVStoreA[A]): KVStoreState[A] =
 9         fa match {
10           case Put(key, value) => State { (s:Map[String, Any]) =>
11              (s.updated(key, value),()) }
12           case Get(key) => State { (s:Map[String, Any]) =>
13             (s,s.get(key).asInstanceOf[A]) }
14           case Del(key) => State { (s:Map[String, Any]) =>
15               (s - key, (())) }
16         }
17     }
18   }

 

1   val testData = Map("What's your first name?" -> "Tiger",
2   "What's your last name?" -> "Chan")             //> testData  : scala.collection.immutable.Map[String,String] = Map(What's your first name? -> Tiger, What's your last name? -> Chan)

测试运算:

点是只拟测试:我们所以个Map[K,V]来效仿互动,K模拟问prompt,V模拟获取回答Input。测试办法是独Function1,输入测试数据Map,在List[Message]里返回所有Tell产生的信。上面提到过Tester[A]必须是单Monad,所以我们兑现了Tester的Monad实例testMonad。实际上
m=>(List,a)就是个writer函数。所谓的Writer就是包嵌一个针对值pair(L,V)的Monad,L代表Log,V代表运算值。Writer的特点就是是log所有V的运算过程。我们又好就此Writer来实现此tester:

接下来我们打一些测试数据:

 

Interact:

/**
   * Same as foldMap but without a guarantee of stack safety. If the recursion is shallow
   * enough, this will work
   */
  final def foldMapUnsafe[M[_]](f: FunctionK[S, M])(implicit M: Monad[M]): M[A] =
    foldMap[M](f)(M, RecursiveTailRecM.create)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图