Cats(1)- 從Free開始,Free cats


  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的其它數據類型進行補充了解。

cats.Free的類型款式如下:

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

S是個高階類,就是一種函數式運算。值得注意的是:現在S不需要是個Functor了。因為Free的一個實例Suspend類型是這樣的:

/** Suspend the computation with the given suspension. */
  private final case class Suspend[S[_], A](a: S[A]) extends Free[S, A]

我們不需要map就可以把F[A]升格成Free

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

 我們在scalaz.Free的討論中並沒能詳盡地分析在什么情況下S[_]必須是個Functor。下面我們需要用一些篇幅來解析。

Free程序的特點是算式(description)/算法(implementation)關注分離(separation of concern):我們用一組數據類型來模擬一種編程語句ADT(algebraic data type),這一組ADT就形成了一種定制的編程語言DSL(domain specific language)。Free的編程部分就是用DSL來描述程序功能(description of purpose),即算式了。算法即用DSL描述的功能的具體實現,可以有多種的功能實現方式。我們先看個簡單的DSL:

 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   }

在這個例子里Interact並不是一個Functor,因為我們無法獲取Interact Functor實例的map函數。先讓我們分析一下Functor的map:

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       }

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調整成下面這樣呢:

 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       }

現在這兩個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。

得出對ADT類型要求結論后,我們接着示范cats的Free編程。下面是Free程序的功能實現interpret部分(implementation):

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   }

DSL程序的功能實現就是把ADT F[A]對應到實際的指令集G[A],在Free編程里用NaturalTransformation ~>來實現。注意G[A]必須是個Monad。在上面的例子里對應關系是:Interact~>Id,代表直接對應到運算指令println和readLine。我們也可以實現另一個版本: 

 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   }

上面是個模擬測試:我們用個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:

 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     }

如果我們用Writer來實現Interact,實際上就是把Ask和Tell都升格成Writer類型。

我們再來看看在cats里是如何運算Free DSL程序的。相對scalaz而言,cats的運算函數簡單的多,就一個foldMap,我們來看看它的定義:

/** * 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))) })

除了要求M是個Monad之外,cats還要求M的RecursiveTailRecM隱式實例。那么什么是RecursiveTailRecM呢:

/** * 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 }

我們用RecursiveTailRecM來保證這個Monad類型與tailRecM是匹配的,這是一種運算安全措施,所以在foldMap函數里r.sameType(M).tailRecM保證了tailRecM不會造成StackOverflowError。cats.Free里還有一種不需要類型安全檢驗的函數foldMapUnsafe:

/** * 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)

這個函數不需要RecursiveTailRecM。下面我們選擇能保證運算安全的方法來運算tester:首先我們需要Tester類型的Monad和RecursiveTailRecM實例:

 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     }

然后我們制造一些測試數據:

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)

測試運算:

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),())

那么如果運算testWriter呢?我們先取得WriterT的Monad實例: 

1    implicit val testWriterMonad =  WriterT.catsDataMonadWriterForWriterT[WF,List[Message]]

然后構建一個RecursiveTailRecM實例后再用同樣的測試數據來運算:

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(())

運算結果一致。

我們再示范一下cats官方文件里關於free monad例子:模擬一個KVStore的put,get,delete功能。ADT設計如下:

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   }

對應的模擬功能函數設計如下:

 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     }

注意一下mod函數:它是由基礎函數get和put組合而成。我們要求所有在for內的類型為Free[KVStoreA,?],所以當f函數施用后如果opt變成None時就返回結果Free.pure(()),它的類型是:Free[Nothing,Unit],Nothing是KVStoreA的子類。

現在我們可以用這個DSL來編制KVS程序了: 

 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   }

我們可以通過State數據結純代碼(pure code)方式來實現用immutable map的KVStore:

 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   }

我們把KVStoreA ADT模擬成對State結構的S轉換(mutation),返回State{S=>(S,A)}。KVStoreState[A]類型的S參數為immutable.Map[String, Any],所以我們在S轉換操作時用immutable map的操作函數來構建新的map返回,典型的pure code。我們來運算一下KVStoreA程序:

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))

但是難道不需要Monad、RecursiveTailRecM實例了嗎?實際上cats已經提供了State的Monad和RecursiveTailRecM實例:

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

在cats的StateT.scala里可以找到這段代碼:

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 } }

我把上面兩個示范的源代碼提供在下面:

Interact:

  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 }

KVStore:

 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 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM