经过了一段时间的学习,我们了解了一系列泛函数据类型。我们知道,在所有编程语言中,数据类型是支持软件编程的基础。同样,泛函数据类型Foldable,Monoid,Functor,Applicative,Traversable,Monad也是我们将来进入实际泛函编程的必需。在前面对这些数据类型的探讨中我们发现:
1、Monoid的主要用途是在进行折叠(Foldable)算法时对可折叠结构内元素进行函数施用(function application)、
2、Functor可以对任何高阶数据类型F[_]内的元素进行普通函数(A => B)施用(map)
3、Applicative extends Functor,同样都是对F[_}内元素进行函数施用。不同的是施用函数是包嵌在高阶类型的(F[A => B])。Applicative可以对所有可游览结构(Traversable),包括可折叠结构(Foldable),嵌入的元素进行函数施用。Applicative好像比Monoid功能更加强大,这样,Applicative的主要用途之一应该是对可游览结构内元素进行函数施用。
4、Monad应该是泛函编程中最重要的数据类型。Monad extends Applicative,这样,Monad就包含了Functor, Applicative的属性。更重要的是,Monad成就了for-comprehension。通过for-comprehension可以实现泛函风格的“行令编程模式(imperative programming)。泛函编程与传统的行令编程在模式上最大的分别就是在泛函编程中没有变量声明(variable declaration),变量是包嵌在一个结构里的(MyData(data)),得申明这个结构(trait MyData[String])。所以泛函编程的命令执行都是在一些结构内部进行的。Monad组件库中的组件主要支持这种结构内部运算风格。无法使用行令编程模式肯定对泛函编程过程造成诸多不便,但Monad使for-comprehension成为可能,而在for-comprehension内可以实现行令编程,所以泛函编程被称为Monadic programming并不为过。
看个for-comprehension例子:
val compute: Option[Int] = { for { x <- getNextNumber x1 <- getNextNumber y <- shapeIt(x) z <- divideBy(y,x1) } yield z }
程序在for{}内一步一步运行,典型的行令模式。
可以说:for-comprehension组成了一个嵌入式的简单行令编程语言,而产生它的Monad同时又确定了它的语意(symatics)。
以上的例子中for-comprehension是由Option[Int]定义的,那么,如果这个for-comprehension是由一个以上Monad组成的呢?例如:IO[Option[A]],这个有点像组合(Monad composition)。那么我们就先从Monad composition开始吧,看怎么把两个Monad compose起来。
怎么compose呢?先看看Functor的composition:
trait Functor[F[_]] { def map[A,B](fa: F[A])(f: A => B): F[B] }
def compose[F[_],G[_]](m: Functor[F], n: Functor[G]) = new Functor[({type l[x] = F[G[x]]})#l] { override def map[A,B](fga: F[G[A]])(f: A => B) = { m.map(fga)(ga => n.map(ga)(f)) } } //> compose: [F[_], G[_]](m: ch12.ex2.Functor[F], n: ch12.ex2.Functor[G])ch12.e //| x2.Functor[[x]F[G[x]]]
val listFunctor = new Functor[List] { override def map[A,B](la: List[A])(f: A => B): List[B] = la.map(f) } //> listFunctor : ch12.ex2.Functor[List] = ch12.ex2$$anonfun$main$1$$anon$6@3c //| bbc1e0 val optionFunctor = new Functor[Option] { override def map[A,B](oa: Option[A])(f: A => B): Option[B] = oa.map(f) } //> optionFunctor : ch12.ex2.Functor[Option] = ch12.ex2$$anonfun$main$1$$anon$ //| 7@35fb3008 Option("abc").map(_.length) //> res4: Option[Int] = Some(3) val fg = compose(listFunctor,optionFunctor) //> fg : ch12.ex2.Functor[[x]List[Option[x]]] = ch12.ex2$$anonfun$main$1$$anon //| $5@7225790e fg.map(List(Option("abc"),Option("xy"),Option("ryuiyty"))){ _.length } //> res5: List[Option[Int]] = List(Some(3), Some(2), Some(7))
那么我们如果能实现Monad[M[N]]的flatMap不就能得到这个Monad实例了嘛:
def composeM[M[_],N[_](m: Monad[M], n: Monad[N]): Monad[({type l[x] = M[N[x]]})#l]= { new Monad[({type l[x] = M[N[x]]})#l] { def flatMap[A,B](mna: M[N[A]])(f: A => M[N[B]]): M[N[B]] = { ????? !!!!! } } }
我们先实现一个Maybe Monad:
Maybe就是Option。由于scala标准库里已经有Option类型,为免函数引用混扰,所以定义一个新的Monad。
trait Functor[F[_]] { def map[A,B](fa: F[A])(f: A => B): F[B] } trait Monad[M[_]] extends Functor[M] { def unit[A](a: A): M[A] def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B] } trait Maybe[+A] { def map[B](f: A => B): Maybe[B] ={ this match { case Just(a) => Just(f(a)) case _ => Nada } } def flatMap[B](f: A => Maybe[B]): Maybe[B] = { this match { case Just(a) => f(a) case _ => Nada } } } case class Just[+A](a: A) extends Maybe[A] case object Nada extends Maybe[Nothing]
val maybeFor: Maybe[Int] = for { x <- Just(2) y <- Just(5) z = x * y } yield z //> maybeFor : ch12.ex2.Maybe[Int] = Just(10)
val maybeMap: Maybe[Int] = { Just(2).flatMap(x => Just(5).map(y => x * y)) //> maybeMap : ch12.ex2.Maybe[Int] = Just(10) }
我们再来一个熟悉的Monad,State Monad:
type State[S,+A] = S => (A,S) object State { def getState[S]: State[S,S] = s => (s,s) def setState[S](s: S): State[S,Unit] = _ => ((),s) } class StateOps[S,A](sa: State[S,A]) { def unit(a: A) = (s: S) => (a,s) def map[B](f: A => B): State[S,B] = { s => { val (a,s1) = sa(s) (f(a),s1) } } def flatMap[B](f: A => State[S,B]): State[S,B] = { s => { val (a,s1) = sa(s) f(a)(s1) } } def getState[S]: State[S,S] = s => (s,s) def setState[S](s: S): State[S,Unit] = _ => ((),s) } implicit def toStateOps[S,A](sa: State[S,A]) = new StateOps(sa) //> toStateOps: [S, A](sa: ch12.ex2.State[S,A])ch12.ex2.StateOps[S,A]
import State._ val stateFor: State[Int, Int] = for { x <- getState[Int] y = x * 5 _ <- setState(x+1) } yield y //> stateFor : ch12.ex2.State[Int,Int] = <function1> stateFor(2) //> res0: (Int, Int) = (10,3)
那我们下面把这两个Monad在一个for-comprehension里运行。比如
val nocompileFor = { def remainder(a: Int, b: Int): Maybe[Int] = { a % b match { case 0 => Nada case r => Just(r) } } for { x <- getState[Int] //State.flatMap y <- remainder(x,2) //Maybe.flatMap z = x + y //???.map _ <- setState[Int](5) //State.flatMap } yield y }
解决方案:Monad Transformer:
上面的失败例子是要解决State[Maybe[A]]这种类型的问题。我们就需要一个State Monad Transformer:
import StateT._ trait StateT[M[_],S,A] { // State Monad Transformer def apply(s: S): M[(A,S)] def map[B](f: A => B)(implicit m: Functor[M]): StateT[M,S,B] = { stateT( s => m.map(apply(s)){ case (a,s1) => (f(a),s1) }) } def flatMap[B](f: A => StateT[M,S,B])(implicit m: Monad[M]): StateT[M,S,B] = { stateT( s => m.flatMap(apply(s)){ case (a,s1) => f(a)(s1) }) } } object StateT { def stateT[M[_],S,A](f: S => M[(A,S)]): StateT[M,S,A] = { new StateT[M,S,A] { def apply(s: S) = f(s) } } def liftM[M[_],S,A](ma: M[A])(implicit m: Monad[M]): StateT[M,S,A] = { stateT(s => m.map(ma)(a => (a, s))) } }
val maybeState: StateT[Maybe,Int,Int] = { def getState[S]: StateT[Maybe,S,S] = stateT(s => Just((s,s))) def setState[S](s: S): StateT[Maybe,S,Unit] = stateT(s1 => Just(((),s))) def remainder(a: Int, b: Int): Maybe[Int] = { a % b match { case 0 => Nada case r => Just(r) } } for { x <- getState[Int] y <- liftM[Maybe,Int,Int](remainder(x,2)) z = x + y _ <- setState[Int](5) } yield y } //> maybeState : ch12.ex2.StateT[ch12.ex2.Maybe,Int,Int] = ch12.ex2$$anonfun$m //| ain$1$StateT$3$$anon$4@34b7bfc0 maybeState(1) //> res1: ch12.ex2.Maybe[(Int, Int)] = Just((1,5)) maybeState(0) //> res2: ch12.ex2.Maybe[(Int, Int)] = Nada
def liftM[M[_],S,A](ma: M[A])(implicit m: Monad[M]): StateT[M,S,A] = { stateT(s => m.map(ma)(a => (a, s))) }
如果我们需要处理相反的类型:Maybe[State],我们就需要定义MaybeT。我们先看看MaybeT的类型款式:
caseclass MaybeT[M[_],A](run: M[Maybe[A]]) 这是Monad Transformer通用款式
我们把共同使用的Monad包嵌在参数里:
case class MaybeT[M[_],A](run: M[Maybe[A]]) { def map[B](f: A => B)(implicit m: Functor[M]): MaybeT[M,B] = { MaybeT[M,B](m.map(run)(a => a map f)) } def flatMap[B](f: A => MaybeT[M,B])(implicit m: Monad[M]): MaybeT[M,B] = { MaybeT[M,B](m.flatMap(run) { case Just(a) => f(a).run case Nada => m.unit(Nada) }) } }
case class OptionT[M[_],A](run: M[Option[A]]) { def map[B](f: A => B)(implicit m: Functor[M]): OptionT[M,B] = { OptionT[M,B](m.map(run)(a => a.map(f))) } def flatMap[B](f: A => OptionT[M,B])(implicit m: Monad[M]): OptionT[M,B] = { OptionT[M,B](m.flatMap(run) { case Some(a) => f(a).run case None => m.unit(None) }) } }