很多函数式编程爱好者都把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]) = fadef 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 }

转载于:https://www.cnblogs.com/tiger-xc/p/5381886.html

Scalaz(38)- Free :Coproduct-Monadic语句组合相关推荐

  1. mysql叠加select,MySQL – 有效地将两个select语句组合成一个...

    您可以将多个查询与UNION组合,但前提是查询具有相同的列数.理想情况下,列不仅在数据类型中,而且在于它们的语义含义;但是,MySQL并不关心语义,并且会通过强制转换为更通用的东西来处理不同的数据类型 ...

  2. 利用ajax,巧妙的sql语句组合,轻松做出不错的树型菜单

    我们的某个项目中,在设计报表时候,考虑到做报表的树型分类,同时考虑到分类下存在子分类和报表并存,使用原有项目中jtree功能已经达不到这要求,因此,考虑蛮久,还是自己写一个吧. 注意: 前提需知: 本 ...

  3. (38)Gulp任务完整组合写法

    一.Gulp任务完整组合写法介绍 我们把less文件编译,CSS代码压缩,JS代码压缩,文件重命名,图片压缩,目录删除的任务都创建好了,那么如果我们一个一个单个去进行执行任务命令,太麻烦了,需要写很多 ...

  4. linux下超强命令(shell语句)组合

    记性很差劲,经常在系统,网络之间转,没有专搞系统.时间一长,所以难免有些命令或组合式语句忘记了,今天把它们集合到一起,以后跑博客上找就是了,陆续更新中... 服务器双网卡,双IP,第二个网卡路由设置格 ...

  5. 经典SQL语句大全、50个常用的sql语句

    50个常用的sql语句 Student(S#,Sname,Sage,Ssex) 学生表 Course(C#,Cname,T#) 课程表 SC(S#,C#,score) 成绩表 Teacher(T#,T ...

  6. mysql语句orderby_mysql中的orderby_MySQL

    一.order by的原理 1.利用索引的有序性获取有序数据 当查询语句的 order BY 条件和查询的执行计划中所利用的 Index 的索引键(或前面几个索引键)完全一致,且索引访问方式为 ran ...

  7. 转:经典SQL语句大全(绝对的经典)

    转:经典SQL语句大全(绝对的经典) 1.经典SQL语句大全(绝对的经典) https://www.cnblogs.com/bluedy1229/p/8992965.html 2. 3. 4.一.基础 ...

  8. 经典SQL操作语句【转载】

    1.经典SQL语句大全(绝对的经典) 2. 3. 4.一.基础 1.1.说明:创建数据库 2.CREATE DATABASE database-name 3.2.说明:删除数据库 4.drop dat ...

  9. 经典常用SQL语句大全(绝对的经典)

    一.基础    1.1.说明:创建数据库   2.CREATE DATABASE database-name    3.2.说明:删除数据库   4.drop database dbname   5. ...

最新文章

  1. python中enumerate在for循环中用法_python中enumerate的用法实例解析
  2. Struts2.x和Struts1.x的区别
  3. 哈尔滨阳光计算机学院是不是黄了,黑龙江这4所野鸡大学,常被误认为是名校,实则害人不浅...
  4. php mysql 执行sql文件_PHP执行SQL文件并将SQL文件导入到数据库_PHP
  5. mac懒人版_这些实用的Mac软件你迟早会用到,建议收藏!
  6. 创建弹出窗口的图片展示
  7. 006-Python迭代器
  8. 开源jshop小程序商城
  9. 389 Find the Difference 找不同
  10. 说说面向对象的故事,主人是人类!(三)
  11. linux谷歌浏览器无法登陆,使用chrome/chrominum浏览器无法正常登陆deepin论坛的解决...
  12. 手机wap浏览器下载选哪家
  13. 服务器如何取得系统管理员权限,技巧:Windows系统如何获得管理员权限?
  14. 图解大数据 | 大数据生态与应用导论
  15. 2019版颱風24、48小時警戒綫(附帶2010版)
  16. Python win8安装
  17. Linux:进程(一)
  18. 动手实现天气预报App(二)——显示天气信息
  19. Android 的媒体路由功能应用与框架解析
  20. svg文件解析(python)

热门文章

  1. 从“做什么”到“怎么做”,说说一只蚊子
  2. Matlab怎样将传递函数转换成差分方程
  3. 无人驾驶的规划与控制(二)——行为决策规划
  4. Python获取主机信息、开机时间和开机时长、当前登陆用户
  5. python爬虫ip代理池_爬虫教程-Python3网络爬虫开发——IP代理池的维护
  6. kubernetes不同的命名空间下的容器能通信吗_在Kubernetes环境中,容器间如何进行网络通信?...
  7. Selenium UI自动化测试(三)IDE—百度个人中心录制实例
  8. Yam旗下Degenerative Finance已上线uSTONKS和uGAS奖励计划
  9. 以太坊2.0质押流动性解决方案Lido上线治理工具Lido DAO
  10. SAP License:ERP系统中供应商管理怎么做?