设为首页 加入收藏

TOP

Scalaz(18)- Monad: ReaderWriterState-可以是一种简单的编程语言(一)
2017-10-10 12:13:28 】 浏览:4249
Tags:Scalaz Monad ReaderWriterState- 可以 简单 编程语言

  说道FP,我们马上会联想到Monad。我们说过Monad的代表函数flatMap可以把两个运算F[A],F[B]连续起来,这样就可以从程序的意义上形成一种串型的流程(workflow)。更直白的讲法是:任何类型只要实现了flatMap就可以用for-comprehension, for{...}yield。在这个for{...}里我们可以好像OOP一样编写程序。这个for就是一种运算模式,它规范了在for{...}里指令的行为。我们正从OOP风格走入FP编程模式,希望有个最基本的FP编程模式使我们能够沿用OOP编程风格的语法和思维。Monad应该就是最合适的泛函数据类型了。我们先从最基本的开始:假如我们有一段行令程序:

/* val a = e1 val b = e2(a) val c = e3(a,b) val d = e2(c) */

通过这些函数e1,e2,e3最后计算出d值。如果是用FP风格来编这段程序的话,首先我们必须把函数的结果d放入F[d]的F里。F就是上面所说的运算模式,在这里可以用大家熟悉的context(上下文)来表示。F必须是个Monad,F[]相当于for{...}yield。我们先试试用Id,虽然Id[A]对A不做任何处理,直接返回,好像没什么意义,但这种类型具备了map和flatMap,应该可以用for-comprehension:

 1 import scalaz._  2 import Scalaz._  3 def e1:Id[Int] = 10                               //> e1: => scalaz.Scalaz.Id[Int]
 4 def e2(a: Int): Id[Int] = a + 1                   //> e2: (a: Int)scalaz.Scalaz.Id[Int]
 5 def e3(a: Int, b: Int): Id[Int] = a + b           //> e3: (a: Int, b: Int)scalaz.Scalaz.Id[Int]
 6 for {  7     a <- e1  8     b <- e2(a)  9     c <- e3(a,b) 10     d <- e2(c) 11 } yield d                                         //> res0: scalaz.Scalaz.Id[Int] = 22

可以看到,在for-loop里就是OOP的行令程序。不过如果觉着这个Id没什么意义,可以试试Option看:

 1 import scalaz._  2 import Scalaz._  3 def e1:Option[Int] = 10.some                      //> e1: => Option[Int]
 4 def e2(a: Int): Option[Int] = (a + 1).some        //> e2: (a: Int)Option[Int]
 5 def e3(a: Int, b: Int): Option[Int] = (a + b).some//> e3: (a: Int, b: Int)Option[Int]
 6 for {  7     a <- e1  8     b <- e2(a)  9     c <- e3(a,b) 10     d <- e2(c) 11 } yield d                                         //> res0: Option[Int] = Some(22)

看,虽然换了个壳子(context), 但for-loop里的程序没有变化。换一句话讲就是for-loop里的程序根本不理会包裹的context。

Reader也是一种Monad,用它又怎样呢:

 1 import scalaz._  2 import Scalaz._  3 def e1:Reader[Int,Int] = Reader[Int,Int](a => a)  //> e1: => scalaz.Reader[Int,Int]
 4 def e2(a: Int): Reader[Int,Int] = Reader[Int,Int](_ => a + 1)  5                                                   //> e2: (a: Int)scalaz.Reader[Int,Int]
 6 def e3(a: Int, b: Int): Reader[Int, Int] = Reader[Int,Int](_ => a+b)  7                                                   //> e3: (a: Int, b: Int)scalaz.Reader[Int,Int]
 8 val prg = for {  9     a <- e1 10     b <- e2(a) 11     c <- e3(a,b) 12     d <- e2(c) 13 } yield d                                         //> prg : scalaz.Kleisli[scalaz.Id.Id,Int,Int] = Kleisli(<function1>)
14 prg.run(10)                                       //> res0: scalaz.Id.Id[Int] = 22

虽然在语法上有些蹩脚,但还是证明了for-loop里的程序是不理会外面context的。那么我们可不可以说这个prg就是一个简单的FP编程语言。它把运算结果放在context里,直至运行了某种interpreter才能取得实际的运算值(用run(10)得到22)。当然,一段程序,它的运算行为受制于单一种类型的context可能有些弱了。如果需要获得一种可用的FP编程语言,我们可能还是要探讨如何把单一类型context组合成多类型混合的context。

我们发现在scalaz里有些type class的名称是以T结束的如:ReaderT,WriterT,StateT等等。这个T指的是变形器Transformer,意思是用它可以堆砌(stacking)context。看看StateT,简单定义应该是这样的: 

case class StateT[F[_],S,A](run: S => F[(S,A)])

我们可以把F类堆砌在State上。实践证明如果这个F实现了flatMap,那么堆砌成的类型也能实现flatMap。好,scalaz的Option是实现了flatMap的,那么能不能把它和State堆砌在一起呢?堆砌而成的context会有什么效果呢?我们先看看单一Option和State作为一种context的效果:

 1 for {  2   a <- 3.some  3   b <- (None: Option[Int])  4   c <- 4.some  5 } yield c                                         //> res1: Option[Int] = None
 6 val statePrg = for {  7   a <- get[Int]  8   b <- State[Int,Int](s => (s, s + a))  9   _ <- put(9) 10 } yield b                                         //> statePrg : scalaz.IndexedStateT[scalaz.Id.Id,Int,Int,Int] = scalaz.IndexedS 11                                                   //| tateT$$anon$10@15ff3e9e
12 statePrg.run(3)                                   //> res2: scalaz.Id.Id[(Int, Int)] = (9,6)

依我来看,Option主要效果是在遇到None值时立即退出。而State的主要作用是在运算同时可以维护一个状态。那么如果把Option和State叠加起来就会同时具备这两种类型的特点了吧?也就是既能维护状态又能在遇到None值时立即终止运算退出了。首先验证一下用Option的flatMap来实现叠加context的flatMap:

 case class OptionState
编程开发网
首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scalaz(17)- Monad:泛函状态.. 下一篇Scalaz(19)- Monad: \/ - Mon..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(214) }