• Scalaz(38)- Free :Coproduct-Monadic语句组合


       很多函数式编程爱好者都把FP称为Monadic Programming,意思是用Monad进行编程。我想FP作为一种比较成熟的编程模式,应该有一套比较规范的操作模式吧。因为Free能把任何F[A]升格成Monad,所以Free的算式(AST)、算法(Interpreter)关注分离(separation of concern)模式应该可以成为一种规范的FP编程模式。我们在前面的几篇讨论中都涉及了一些AST的设计和运算,但都是一些功能单一,离散的例子。如果希望通过Free获取一个完整可用的程序,就必须想办法把离散的Free AST组合成一体运算。我们先从单一的Free AST例子开始:

     1 import scalaz._
     2 import Scalaz._
     3 import scala.language.higherKinds 
     4 import scala.language.implicitConversions
     5 object FreeModules {
     6   object FreeInteract {
     7     trait Interact[+A]
     8     type FreeInteract[A] = Free.FreeC[Interact,A]
     9     object Interact {
    10       case class Ask(prompt: String) extends Interact[String]
    11       case class Tell(msg: String) extends Interact[Unit]
    12       implicit def interactToFreeC[A](ia: Interact[A]) = Free.liftFC(ia)
    13       object InteractConsole extends (Interact ~> Id) {
    14         def apply[A](ia: Interact[A]): Id[A] = ia match {
    15           case Ask(p) => println(p); readLine
    16           case Tell(m) => println(m)
    17         }
    18       }
    19     }
    20     import Interact._
    21     val interactScript = for {
    22       first <- Ask("What's your first name?")
    23       last <- Ask("What's your last name?")
    24       _ <- Tell(s"Hello ${first} ${last}, nice to meet you!")
    25     } yield ()
    26   }
    27 }

    这是一个我们在前面讨论中重复描述几次的简单交互例子,包括了ADT、AST和Interpreter。我们可以直接运行这个程序:

    1 object freePrgDemo extends App {
    2   import FreeModules._
    3   import FreeInteract._
    4   import Interact._
    5   Free.runFC(interactScript)(InteractConsole)  
    6 }

    运算结果如下:

    1 What's your first name?
    2 Tiger
    3 What's your last name?
    4 Chan
    5 Hello Tiger Chan, nice to meet you!

    就是简单的两句界面提示和键盘输入,然后提示输入结果,没什么意义。作为测试,我们也可以模拟Console交互:用Map[String,String]来模拟Map[提问,回答],然后把这个Map提供给Interpreter,返回结果(List[String],A),其中List[String]是运行跟踪记录,A是模拟的键盘输入:

     1       type InteractMapTester[A] = Map[String,String] => (List[String], A)
     2       implicit val mapTesterMonad = new Monad[InteractMapTester] {
     3          def point[A](a: => A) = _ => (List(), a)
     4          def bind[A,B](ia: InteractMapTester[A])(f: A => InteractMapTester[B]): InteractMapTester[B] =
     5            m => {
     6              val (o1,a1) = ia(m)
     7              val (o2,a2) = f(a1)(m)
     8              (o1 ++ o2, a2)
     9            }
    10       }
    11       object InteractTesterMap extends (Interact ~> InteractMapTester) {
    12         def apply[A](ia: Interact[A]): InteractMapTester[A] = ia match {
    13           case Ask(p) => { m => (List(), m(p)) } //m(p)返回提问对应的答案作为键盘输入
    14           case Tell(s) => { m => (List(s), ()) } //List(s)在bind函数中的o1++o2形成跟踪记录
    15                                                  //在运算AST时就会调用InteractMapTester的bind函数
    16         }
    17       }

    使用模拟Console的Interpreter来运行:

     1 object freePrgDemo extends App {
     2   import FreeModules._
     3   import FreeInteract._
     4   import Interact._
     5   //Free.runFC(interactScript)(InteractConsole) 
     6     val result = Free.runFC(interactScript)(InteractTesterMap).apply(
     7     Map(
     8     "What's your first name?" -> "tiger",
     9     "What's your last name?" -> "chan"
    10   ))
    11   println(result)
    12   }
    13 //产生以下输出结果
    14 (List(Hello tiger chan, nice to meet you!),())

    从mapTesterMonad定义中的bind看到了这句:o1++o2,是Logger的典型特征。那么用Writer能不能实现同等效果呢?我们先看看WriterT:

    final case class WriterT[F[_], W, A](run: F[(W, A)]) { self =>
    ...

    实际上这个W就可以满足Logger的功能,因为在WriterT的flatMap中实现了W|+|W:

      def flatMap[B](f: A => WriterT[F, W, B])(implicit F: Bind[F], s: Semigroup[W]): WriterT[F, W, B] =
        flatMapF(f.andThen(_.run))
    
      def flatMapF[B](f: A => F[(W, B)])(implicit F: Bind[F], s: Semigroup[W]): WriterT[F, W, B] =
        writerT(F.bind(run){wa =>
          val z = f(wa._2)
          F.map(z)(wb => (s.append(wa._1, wb._1), wb._2))
        })

    那么如何把Map[提问,回答]传人呢?我们可以通过WriterT[F[_],W,A]的F[]来实现这一目的:

    1       type WriterTF[A] = Map[String,String] => A
    2       type InteractWriterTester[A] = WriterT[WriterTF,List[String],A]

    然后我们可以用WriterT的参数run来传人Map[String,String]:run:WriterTF[(W,A)] == Map[String,String]=>(W,A)。

    以下是用WriterT实现的Interpreter版本:

     1       type WriterTF[A] = Map[String,String] => A
     2       type InteractWriterTester[A] = WriterT[WriterTF,List[String],A]
     3       def testerToWriter[A](f: Map[String,String] => (List[String], A)) = 
     4         WriterT[WriterTF,List[String],A](f)
     5       implicit val writerTesterMonad = WriterT.writerTMonad[WriterTF, List[String]]
     6       object InteractTesterWriter extends (Interact ~> InteractWriterTester) {
     7         def apply[A](ia: Interact[A]): InteractWriterTester[A] = ia match {
     8           case Ask(p) => testerToWriter { m => (List(), m(p)) }
     9           case Tell(s) => testerToWriter { m => (List(s), ())}
    10         }
    11       }

    我们可以这样运行:

    object freePrgDemo extends App {
      import FreeModules._
      import FreeInteract._
      import Interact._
      //Free.runFC(interactScript)(InteractConsole) 
      //val result = Free.runFC(interactScript)(InteractTesterMap).apply(
      val result = Free.runFC(interactScript)(InteractTesterWriter).run(
        Map(
        "What's your first name?" -> "tiger",
        "What's your last name?" -> "chan"
      )) 
      println(result)
      
    }

    我们再设计另一个用户登录Login的例子:

     1   object FreeUserLogin {  
     2     import Dependencies._
     3     trait UserLogin[+A]
     4     type FreeUserLogin[A] = Free.FreeC[UserLogin,A]
     5     object UserLogin {
     6       case class Login(user: String, pswd: String) extends UserLogin[Boolean]
     7       implicit def loginToFree[A](ul: UserLogin[A]) = Free.liftFC(ul)
     8       type LoginService[A] = Reader[PasswordControl,A]
     9       object LoginInterpreter extends (UserLogin ~> LoginService) {
    10         def apply[A](ul: UserLogin[A]): LoginService[A] = ul match {
    11           case Login(u,p) => Reader( cr => cr.matchPassword(u, p))
    12         }
    13       }
    14     }
    15     import UserLogin._
    16     val loginScript = for {
    17       b <- Login("Tiger","1234")
    18     } yield b
    19   }

    这个例子里只有Login一个ADT,它的功能是把输入的User和Password与一个用户登录管理系统内的用户身份信息进行验证。由于如何进行用户密码验证不是这个ADT的功能,它可能涉及另一特殊功能系统的调用,刚好用来做个Reader依赖注入示范。以下是这项依赖定义:

    1 object Dependencies {
    2   trait PasswordControl {
    3     type User = String
    4     type Password = String
    5     val pswdMap: Map[User, Password]
    6     def matchPassword(u: User, p: Password): Boolean
    7   }
    8 }

    对loginScript进行测试运算时必须先获取PasswordControl实例,然后注入运算:

     1   import Dependencies._
     2   import FreeUserLogin._
     3   import UserLogin._
     4   object Passwords extends PasswordControl {  //依赖实例
     5      val pswdMap = Map (
     6        "Tiger" -> "1234",
     7        "John" -> "0332"
     8      )
     9      def matchPassword(u: User, p: Password) = pswdMap.getOrElse(u, p+"!") === p
    10   } 
    11   val result = Free.runFC(loginScript)(LoginInterpreter).run(Passwords)  //注入依赖
    12   println(result)

    不过即使能够运行,loginScsript的功能明显不完整,还需要像Interact那样的互动部分来获取用户输入信息。那么我们是不是考虑在ADT层次上把Interact和UserLogin合并起来,像这样:

    1       case class Ask(prompt: String) extends Interact[String]
    2       case class Tell(msg: String) extends Interact[Unit]
    3       case class Login(user: String, pswd: String) extends Interact[Boolean]

    明显这是可行的。但是,Interact和Login被紧紧捆绑在了一起形成了一个新的ADT。如果我们设计另一个同样需要互动的ADT,我们就需要重复同样的Interact功能设计,显然这样做违背了FP的原则:从功能单一的基本计算开始,按需要对基本函数进行组合实现更复杂的功能。Interact和UserLogin都是基础ADT,从编程语言角度描述Interact和UserLogin属于两种类型的编程语句。我们最终需要的AST是这样的:

    1   val interLogin: Free[???, A] = for {
    2     user <- Ask("Enter User ID:")  //Free[Interact,A]
    3     pswd <- Ask("Enter Password:") //Free[Interact,A]
    4     ok <- Login(user,pswd) //Free[UserLogin,A]
    5   } yield ok

    不过明显类型对不上,因为Interact和UserLogin是两种语句。scalaz的Coproduct类型可以帮助我们实现两种Monadic语句的语义(sematics)合并。Coproduct是这样定义的:scalaz/Coproduct.scala

    /** `F` on the left, and `G` on the right, of [[scalaz./]].
      *
      * @param run The underlying [[scalaz./]]. */
    final case class Coproduct[F[_], G[_], A](run: F[A] / G[A]) {
      import Coproduct._
    
      def map[B](f: A => B)(implicit F: Functor[F], G: Functor[G]): Coproduct[F, G, B] =
        Coproduct(run.bimap(F.map(_)(f), G.map(_)(f)))
    ...

    从run:F[A]/G[A]可以理解Coproduct是两种语句F,G的联合(union)。在我们上面的例子里我们可以用下面的表达方式代表Interact和UserLogin两种语句的联合(union):

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

    这是一个语义更广泛的类型:包含了Interact和UserLogin语义。我们可以用Inject类型来把Interact和UserLogin语句集“注入”到一个更大的句集。Inject是这样定义的:scalaz/Inject.scala

    /**
     * Inject type class as described in "Data types a la carte" (Swierstra 2008).
     *
     * @see [[http://www.staff.science.uu.nl/~swier004/Publications/DataTypesALaCarte.pdf]]
     */
    sealed abstract class Inject[F[_], G[_]] {
      def inj[A](fa: F[A]): G[A]
      def prj[A](ga: G[A]): Option[F[A]]
    }
    
    sealed abstract class InjectInstances {
      implicit def reflexiveInjectInstance[F[_]] =
        new Inject[F, F] {
          def inj[A](fa: F[A]) = fa
          def prj[A](ga: F[A]) = some(ga)
        }
    
      implicit def leftInjectInstance[F[_], G[_]] =
        new Inject[F, ({type λ[α] = Coproduct[F, G, α]})#λ] {
          def inj[A](fa: F[A]) = Coproduct.leftc(fa)
          def prj[A](ga: Coproduct[F, G, A]) = ga.run.fold(some(_), _ => none)
        }
    
      implicit def rightInjectInstance[F[_], G[_], H[_]](implicit I: Inject[F, G]) =
          new Inject[F, ({type λ[α] = Coproduct[H, G, α]})#λ] {
            def inj[A](fa: F[A]) = Coproduct.rightc(I.inj(fa))
            def prj[A](ga: Coproduct[H, G, A]) = ga.run.fold(_ => none, I.prj(_))
          }
    }
    ...

    实现函数inj(fa:F[A]):G[A]代表把F[A]并入G[A]。这里还提供了三个类型的实例:

    1、reflexiceInjectInstance[F[_]]:自我注入

    2、leftInjectInstance[F[_],G[_]]:把F[A]注入Coproduct[F,G,A]的left(-/)

    3、rightInjectInstance[F[_],G[_],H[_]]:把F[A]注入Coproduct的right(/-)。需要先把F注入G(inj(F[A]):G[A])

    我们可以用implicitly来证明Interact和UserLogin的Inject实例存在:

    1   val selfInj = implicitly[Inject[Interact,Interact]]
    2   type LeftInterLogin[A] = Coproduct[Interact,UserLogin,A]
    3   val leftInj = implicitly[Inject[Interact,LeftInterLogin]]
    4   type RightInterLogin[A] = Coproduct[UserLogin,LeftInterLogin,A]
    5   val rightInj = implicitly[Inject[Interact,RightInterLogin]]

    现在我们需要把Coproduct[F,G,A]的F与G合并然后把F[A]升格成Free[G,A]:

    1   object coproduct {
    2     def lift[F[_],G[_],A](fa: F[A])(implicit I: Inject[F,G]): Free.FreeC[G,A] = Free.liftFC(I.inj(fa))
    3   }

    我们可以用这个lift把Interact和UserLogin的ADT统一升格成Free[G,A]:

     1   object coproduct {
     2     import FreeInteract._
     3     import Interact._
     4     import FreeUserLogin._
     5     import UserLogin._
     6     def lift[F[_],G[_],A](fa: F[A])(implicit I: Inject[F,G]): Free.FreeC[G,A] = Free.liftFC(I.inj(fa))
     7     class Interacts[G[_]](implicit I: Inject[Interact,G]) {
     8       def ask(prompt: String): Free.FreeC[G,String] = lift(Ask(prompt))
     9       def tell(msg: String): Free.FreeC[G,Unit] = lift(Tell(msg))
    10     }
    11     class Logins[G[_]](implicit I: Inject[UserLogin,G]) {
    12       def login(u: String, p: String): Free.FreeC[G,Boolean] = lift(Login(u,p))
    13     }
    14   }

    我们用lift把基础Interact和UserLogin的语句注入了联合的语句集G[A],然后升格成FreeC[G,A]。现在我们可以把Interact,UserLogin这两种语句用在同一个for-comprehension里了:

     1   def loginScript[G[_]](implicit I: Interacts[G], L: Logins[G]) ={
     2     import I._
     3     import L._
     4     for {
     5       uid <- ask("ya id?")
     6       pwd <- ask("password?")
     7       login <- login(uid,pwd)
     8       _ <- if (login) tell("ya lucky bastard!") else tell("geda fk outa here!")
     9     } yield()
    10   }

    有了Inject和Lift,现在已经成功的用两种ADT集成了一个AST。不过我们还必须提供Interacts[G]和Logins[G]实例:

     1 object CoproductModules {
     2   object CoproductFunctions {
     3     import FreeInteract._
     4     import Interact._
     5     import FreeUserLogin._
     6     import UserLogin._
     7     def lift[F[_],G[_],A](fa: F[A])(implicit I: Inject[F,G]): Free.FreeC[G,A] = Free.liftFC(I.inj(fa))
     8     class Interacts[G[_]](implicit I: Inject[Interact,G]) {
     9       def ask(prompt: String): Free.FreeC[G,String] = lift(Ask(prompt))
    10       def tell(msg: String): Free.FreeC[G,Unit] = lift(Tell(msg))
    11     }
    12     object Interacts {
    13       implicit def instance[G[_]](implicit I: Inject[Interact,G]) = new Interacts[G]
    14     }
    15     class Logins[G[_]](implicit I: Inject[UserLogin,G]) {
    16       def login(u: String, p: String): Free.FreeC[G,Boolean] = lift(Login(u,p))
    17     }
    18     object Logins {
    19       implicit def instance[G[_]](implicit I: Inject[UserLogin,G]) = new Logins[G]
    20     }
    21   }

    现在我们的语句集(AST)是一个联合的语句集(Coproduct)。那么,我们应该怎么去运算它呢?我们应该如何实现它的Interpreter?现在我们面对的Monadic程序类型是个Coproduct:

    1   type InteractLogin[A] = Coproduct[Interact,UserLogin,A]
    2   val loginPrg = loginScript[InteractLogin]

    现在语句集Interact和UserLogin是分别放在Coproduce的左右两边。那么我们可以历遍这个Coproduct来分别运算Interact和UserLogin语句:

    1   def or[F[_],G[_],H[_]](fg: F ~> G, hg: H ~> G): ({type l[x] = Coproduct[F,H,x]})#l ~> G =
    2     new (({type l[x] = Coproduct[F,H,x]})#l ~> G) {
    3     def apply[A](ca: Coproduct[F,H,A]): G[A] = ca.run match {
    4       case -/(fa) => fg(fa)
    5       case /-(ha) => hg(ha)
    6     }
    7   }

    值得注意的是如果or函数用在Interact和UserLogin上时它们自然转换(NaturalTransformation)的目标类型必须一致,应该是一个更大的类型,而且必须是Monad,这是NaturalTransformation的要求。所以我们可以把InteractInterpreter的转换目标类型由Id变成Reader,也就是LoginInterpreter的转换目标类型:

    1   object InteractReader extends (Interact ~> LoginService) {
    2     def apply[A](ia: Interact[A]): LoginService[A] = ia match {
    3     case Ask(p) => println(p);  Reader(cr => readLine)
    4     case Tell(m) => println(m); Reader(cr => ())
    5    }
    6   }      

    好了,现在我们可以这样来测试运算:

     1 object freePrgDemo extends App {
     2   import FreeModules._
     3   import FreeInteract._
     4   import Interact._
     5   //Free.runFC(interactScript)(InteractConsole) 
     6   //val result = Free.runFC(interactScript)(InteractTesterMap).apply(
     7  /* val result = Free.runFC(interactScript)(InteractTesterWriter).run(
     8     Map(
     9     "What's your first name?" -> "tiger",
    10     "What's your last name?" -> "chan"
    11   )) 
    12   println(result)
    13   */
    14   import Dependencies._
    15   import FreeUserLogin._
    16   import UserLogin._
    17   
    18   object Passwords extends PasswordControl {
    19      val pswdMap = Map (
    20        "Tiger" -> "1234",
    21        "John" -> "0332"
    22      )
    23      def matchPassword(u: User, p: Password) = pswdMap.getOrElse(u, p+"!") === p
    24   } 
    25   /*
    26   val result = Free.runFC(loginScript)(LoginInterpreter).run(Passwords)
    27   println(result)
    28   */
    29   
    30   import CoproductDemo._
    31   Free.runFC(loginPrg)(or(InteractReader,LoginInterpreter)).run(Passwords)
    32 }

    我们把密码管理依赖也注入进去了。看看结果:

     1 ya id?
     2 Tiger
     3 password?
     4 2012
     5 geda fk outa here!
     6 
     7 ya id?
     8 Tiger
     9 password?
    10 1234
    11 ya lucky bastard!
    12 
    13 ya id?
    14 John
    15 password?
    16 0332
    17 ya lucky bastard!

    OK, 把这节示范源代码提供在下面:

      1 package demos
      2 import scalaz._
      3 import Scalaz._
      4 import scala.language.higherKinds 
      5 import scala.language.implicitConversions
      6 object FreeModules {
      7   object FreeInteract {
      8     trait Interact[+A]
      9     type FreeInteract[A] = Free.FreeC[Interact,A]
     10     object Interact {
     11       case class Ask(prompt: String) extends Interact[String]
     12       case class Tell(msg: String) extends Interact[Unit]
     13       implicit def interactToFreeC[A](ia: Interact[A]) = Free.liftFC(ia)
     14       object InteractConsole extends (Interact ~> Id) {
     15         def apply[A](ia: Interact[A]): Id[A] = ia match {
     16           case Ask(p) => println(p); readLine
     17           case Tell(m) => println(m)
     18         }
     19       }      
     20       type InteractMapTester[A] = Map[String,String] => (List[String], A)
     21       implicit val mapTesterMonad = new Monad[InteractMapTester] {
     22          def point[A](a: => A) = _ => (List(), a)
     23          def bind[A,B](ia: InteractMapTester[A])(f: A => InteractMapTester[B]): InteractMapTester[B] =
     24            m => {
     25              val (o1,a1) = ia(m)
     26              val (o2,a2) = f(a1)(m)
     27              (o1 ++ o2, a2)
     28            }
     29       }
     30       object InteractTesterMap extends (Interact ~> InteractMapTester) {
     31         def apply[A](ia: Interact[A]): InteractMapTester[A] = ia match {
     32           case Ask(p) => { m => (List(), m(p)) } //m(p)返回提问对应的答案作为键盘输入
     33           case Tell(s) => { m => (List(s), ()) } //List(s)在bind函数中的o1++o2形成跟踪记录
     34                                                  //在运算AST时会用到InteractMapTester的bind
     35         }
     36       }
     37       type WriterTF[A] = Map[String,String] => A
     38       type InteractWriterTester[A] = WriterT[WriterTF,List[String],A]
     39       def testerToWriter[A](f: Map[String,String] => (List[String], A)) = 
     40         WriterT[WriterTF,List[String],A](f)
     41       implicit val writerTesterMonad = WriterT.writerTMonad[WriterTF, List[String]]
     42       object InteractTesterWriter extends (Interact ~> InteractWriterTester) {
     43         def apply[A](ia: Interact[A]): InteractWriterTester[A] = ia match {
     44           case Ask(p) => testerToWriter { m => (List(), m(p)) }
     45           case Tell(s) => testerToWriter { m => (List(s), ())}
     46         }
     47       }
     48     }
     49     import Interact._
     50     val interactScript = for {
     51       first <- Ask("What's your first name?")
     52       last <- Ask("What's your last name?")
     53       _ <- Tell(s"Hello ${first} ${last}, nice to meet you!")
     54     } yield ()
     55   }
     56   object FreeUserLogin {  
     57     import Dependencies._
     58     trait UserLogin[+A]
     59     type FreeUserLogin[A] = Free.FreeC[UserLogin,A]
     60     object UserLogin {
     61       case class Login(user: String, pswd: String) extends UserLogin[Boolean]
     62       implicit def loginToFree[A](ul: UserLogin[A]) = Free.liftFC(ul)
     63       type LoginService[A] = Reader[PasswordControl,A]
     64       object LoginInterpreter extends (UserLogin ~> LoginService) {
     65         def apply[A](ul: UserLogin[A]): LoginService[A] = ul match {
     66           case Login(u,p) => Reader( cr => cr.matchPassword(u, p))
     67         }
     68       }
     69     }
     70     import UserLogin._
     71     val loginScript = for {
     72       b <- Login("Tiger","1234")
     73     } yield b
     74   }
     75 }
     76 object Dependencies {
     77   trait PasswordControl {
     78     type User = String
     79     type Password = String
     80     val pswdMap: Map[User, Password]
     81     def matchPassword(u: User, p: Password): Boolean
     82   }
     83 }
     84 object CoproductDemo {
     85   import FreeModules._
     86   import FreeUserLogin._
     87   import UserLogin._
     88   import FreeInteract._
     89   import Interact._
     90   import Dependencies._
     91   def lift[F[_],G[_],A](fa: F[A])(implicit I: Inject[F,G]): Free.FreeC[G,A] = Free.liftFC(I.inj(fa))
     92   class Interacts[G[_]](implicit I: Inject[Interact,G]) {
     93     def ask(prompt: String) = lift(Ask(prompt))
     94     def tell(msg: String) = lift(Tell(msg))
     95   }
     96   object Interacts {
     97     implicit def instance[F[_]](implicit I: Inject[Interact,F]) = new Interacts[F]
     98   }
     99   class Logins[G[_]](implicit I: Inject[UserLogin,G]) {
    100     def login(user: String, pswd: String) = lift(Login(user,pswd))
    101   }
    102   object Logins {
    103     implicit def instance[F[_]](implicit I: Inject[UserLogin,F]) = new Logins[F]
    104   }
    105   def loginScript[G[_]](implicit I: Interacts[G], L: Logins[G]) ={
    106     import I._
    107     import L._
    108     for {
    109       uid <- ask("ya id?")
    110       pwd <- ask("password?")
    111       login <- login(uid,pwd)
    112       _ <- if (login) tell("ya lucky bastard!") else tell("geda fk outa here!")
    113     } yield()
    114   }
    115   
    116   def or[F[_],G[_],H[_]](fg: F ~> G, hg: H ~> G): ({type l[x] = Coproduct[F,H,x]})#l ~> G =
    117     new (({type l[x] = Coproduct[F,H,x]})#l ~> G) {
    118     def apply[A](ca: Coproduct[F,H,A]): G[A] = ca.run match {
    119       case -/(fa) => fg(fa)
    120       case /-(ha) => hg(ha)
    121     }
    122   }
    123  
    124   type InteractLogin[A] = Coproduct[Interact,UserLogin,A]
    125   val loginPrg = loginScript[InteractLogin]
    126   object InteractReader extends (Interact ~> LoginService) {
    127     def apply[A](ia: Interact[A]): LoginService[A] = ia match {
    128     case Ask(p) => println(p);  Reader(cr => readLine)
    129     case Tell(m) => println(m); Reader(cr => ())
    130    }
    131   }      
    132  
    133 }
    134 
    135 object freePrgDemo extends App {
    136   import FreeModules._
    137   import FreeInteract._
    138   import Interact._
    139   //Free.runFC(interactScript)(InteractConsole) 
    140   //val result = Free.runFC(interactScript)(InteractTesterMap).apply(
    141  /* val result = Free.runFC(interactScript)(InteractTesterWriter).run(
    142     Map(
    143     "What's your first name?" -> "tiger",
    144     "What's your last name?" -> "chan"
    145   )) 
    146   println(result)
    147   */
    148   import Dependencies._
    149   import FreeUserLogin._
    150   import UserLogin._
    151   
    152   object Passwords extends PasswordControl {
    153      val pswdMap = Map (
    154        "Tiger" -> "1234",
    155        "John" -> "0332"
    156      )
    157      def matchPassword(u: User, p: Password) = pswdMap.getOrElse(u, p+"!") === p
    158   } 
    159   /*
    160   val result = Free.runFC(loginScript)(LoginInterpreter).run(Passwords)
    161   println(result)
    162   */
    163   
    164   import CoproductDemo._
    165   Free.runFC(loginPrg)(or(InteractReader,LoginInterpreter)).run(Passwords)
    166 }

     

     

  • 相关阅读:
    Triangle
    Pascal's Triangle II
    Pascal's Triangle
    Populating Next Right Pointers in Each Node II
    Populating Next Right Pointers in Each Node
    [c++]this指针理解
    [oracle]一个最简单的oracle存储过程"proc_helloworld"
    Oracle 的 INSERT ALL和INSERT FIRST
    Linux2.6 内核的 Initrd 机制解析
    /boot/grub/menu.lst详解
  • 原文地址:https://www.cnblogs.com/tiger-xc/p/5381886.html
Copyright © 2020-2023  润新知