泛函编程(25)-泛函数据类型-Monad-Applicative泛函编程(27)-泛函编程模式-Monad Transformer

   
上点儿冀我们讨论了Monad。我们说Monad是单极度有概括性(抽象性)的泛函数据类型,它可以覆盖绝大多数数据类型。任何数据类型只要能促成flatMap+unit这组Monad最基本组件函数就好改为Monad实例,就可使Monad组件库像for-comprehension这样非常的、Monad具备的泛函式数据结构内部的按序计算运行流程。针对不同的数据类型,flatMap+unit组件实现方式会有所不同,这是盖flatMap+unit代表在承上启下数据类型特别的计量行为。之前我们尝试了List,Option,甚至更复杂的State等数据类型的Monad实例,过程遭到我们独家针对这些数据类型的unit和flatMap进行了贯彻。实际上flatMap+unit并无是Monad唯一的最基本组件函数,还有compose+unit及join+map+unit这片组Monad最中心组件函数,因为我们得为此这些零部件相互实现:

   
经过了一段时间的就学,我们了解了同一多样泛函数据类型。我们掌握,在具备编程语言中,数据类型是支持软件编程的基础。同样,泛函数据类型Foldable,Monoid,Functor,Applicative,Traversable,Monad也是咱前进入实际泛函编程的必不可少。在前方对这些数据类型的追究中我们发现:

 1   trait Monad[M[_]] {
 2       def unit[A](a: A): M[A]
 3       def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B]
 4       def compose[A,B,C](f: A => M[B], g: B => M[C]): A => M[C] = {
 5           a => { flatMap(f(a))(g)}
 6       }
 7       def flatMapByCompose[A,B](ma: M[A])(f: A => M[B]): M[B] = {
 8           compose(((_):Unit) => ma,f)(())
 9       }
10       def join[A](mma: M[M[A]]): M[A] = {
11           flatMap(mma)(ma => ma)
12       }
13       def map[A,B](ma: M[A])(f: A => B): M[B] = {
14           flatMap(ma)(a => unit(f(a)))
15       }
16       def flatMapByJoin[A,B](ma: M[A])(f: A => M[B]): M[B] = {
17           join(map(ma)(a => f(a)))
18       }
19       def composeByJoin[A,B,C](f: A => M[B], g: B => M[C]): A => M[C] = {
20           a => { join(map(f(a))(g)) }
21       }
22   }

1、Monoid的主要用途是以进行折叠(Foldable)算法时对可折结构内元素进行函数施用(function
application)、

据此,我们可由此一直针对数据类型实现join+map+unit或compose+unit来出Monad实例。

2、Functor可以本着任何高阶数据类型F[_]内的素进行日常函数(A
=> B)施用(map)

Monad也是Functor,因为我们得以为此flatMap+unit来兑现map。现在咱们得拿Monad
trait 改化 extends Functor:

3、Applicative extends
Functor,同样都是对准F[_}内元素进行函数施用。不同之是以函数是包嵌在高阶类型的(F[A
=>
B])。Applicative可以针对所有可畅游结构(Traversable),包括但折结构(Foldable),嵌入的因素进行函数施用。Applicative好像比较Monoid功能更加有力,这样,Applicative的主要用途之一应该是指向而观光结构内元素进行函数施用。

1   trait Functor[F[_]] {
2       def map[A,B](fa: F[A])(f: A => B): F[B]
3   }
4   trait Monad[M[_]] extends Functor[M]{
5       def unit[A](a: A): M[A]
6       def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B]
7       def map[A,B](ma: M[A])(f: A => B): M[B] = {
8           flatMap(ma)(a => unit(f(a)))
9       }

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并不为过。

鉴于Monad是个超概括的数据类型,必须配合各种计算模式,无法只顾针对有特殊的操作模式。在泛函编程模式受到极其具特色之饶是在一个查封构造外运行函数。其中较显著的便是map2这个函数了:

看个for-comprehension例子:

1       def map2[A,B,C](ma: M[A], mb: M[B])(f: (A,B) => C): M[C] = {
2           flatMap(ma)(a => map(mb)(b => f(a,b)))
3       }
1   val compute: Option[Int] = {
2    for {
3        x <- getNextNumber
4        x1 <- getNextNumber
5        y <- shapeIt(x)
6        z <- divideBy(y,x1)
7    } yield z
8   }

map2把简单个封于高阶类型结构里的因素通过运行f函数结合起来。完成操作后有的结果还是保持结构的完整性。这是同等栽典型的泛函编程函数施用模式(idiomatic
function
application)。由于这种函数施用模式于泛函编程中使用非常普遍,所以我们专门将这种模式之零部件库独立出来并称之为Applicative。从前方的座谈我们好小心到博数据类型Monad实例的组件函数都可以就此map2和unit来实现,如: 

次第于for{}内同样步一步运行,典型的行令模式。

1       def sequence[A](lma: List[M[A]]): M[List[A]] = {
2           lma.foldRight(unit(List[A]()))((a,ma) => map2(a,ma)(_ :: _))
3       }
4       def traverse[A,B](la: List[A])(f: A => M[B]): M[List[B]] = {
5           la.foldRight(unit(List[B]()))((a,mb) => map2(f(a),mb)(_ :: _))
6       }

可以说:for-comprehension组成了一个嵌入式的简约行令编程语言,而发它的Monad同时还要规定了它们的语意(symatics)。

尽管如此于咱们的例证里map2是经过flatMap+map实现的,实际上很多数据类型都得以一直促成map2,就像这样:

以上之事例中for-comprehension是由于Option[Int]概念之,那么,如果是for-comprehension是由于一个以上Monad组成的为?例如:IO[Option[A]],这个略带像做(Monad
composition)。那么我们尽管先行由Monad composition开始吧,看怎么管个别只Monad
compose起来。

1  def Map2[A,B,C](ma: Option[A], mb: Option[B])(f: (A,B) => C): Option[C] = {
2       (ma,mb) match {
3           case (Some(a),Some(b)) => Some(f(a,b))
4           case _ => None
5       }
6   }

怎么compose呢?先看看Functor的composition:

那map2+unit会不会见呢是Monad的极度基本组件为?答案是否定的,因为用map2+unit是无能为力兑现flatMap、join及compose的。

 1     trait Functor[F[_]] {
 2         def map[A,B](fa: F[A])(f: A => B): F[B]
 3     }
 4   def compose[F[_],G[_]](m: Functor[F], n: Functor[G]) =
 5       new Functor[({type l[x] = F[G[x]]})#l] {
 6           override def map[A,B](fga: F[G[A]])(f: A => B) = {
 7               m.map(fga)(ga => n.map(ga)(f))
 8           }
 9       }                                           //> compose: [F[_], G[_]](m: ch12.ex2.Functor[F], n: ch12.ex2.Functor[G])ch12.e
10                                                   //| x2.Functor[[x]F[G[x]]]

因咱们能用flatMap来实现map2,所以Monad就是Applicative。但反之Applicative不必然是Monad。既然我们希望加强泛函施用模式的频率,那咱们就算优先打函数施用开始。先看map,map2,flatMap这三个函数:

咱们懂得:只要实现了纸上谈兵函数map,就好形成Functor实例。这个Functor[({type l[x]
= F[G[x]]})#l]虽是一个Functor实例,因为咱们可以实现map[A,B](fga:
F[G[A]])(f: A =>
B)。有矣是Functor实例,我们就是好处理F[G[A]]然类型的数据类型:

1   def map[A,B]      (ma: M[A])       (f: A => B)    : M[B]
2   def map2[A,B,C](ma: M[A], mb: M[B])(f: (A,B) => C): M[C]
3   def flatMap[A,B]  (ma: M[A])       (f: A => M[B]) : M[B]
 1  val listFunctor = new Functor[List] {
 2       override def map[A,B](la: List[A])(f: A => B): List[B] = la.map(f)
 3   }                                               //> listFunctor  : ch12.ex2.Functor[List] = ch12.ex2$$anonfun$main$1$$anon$6@3c
 4                                                   //| bbc1e0
 5   val optionFunctor = new Functor[Option] {
 6       override def map[A,B](oa: Option[A])(f: A => B): Option[B] = oa.map(f)
 7   }                                               //> optionFunctor  : ch12.ex2.Functor[Option] = ch12.ex2$$anonfun$main$1$$anon$
 8                                                   //| 7@35fb3008
 9   
10   Option("abc").map(_.length)                     //> res4: Option[Int] = Some(3)
11   val fg = compose(listFunctor,optionFunctor)     //> fg  : ch12.ex2.Functor[[x]List[Option[x]]] = ch12.ex2$$anonfun$main$1$$anon
12                                                   //| $5@7225790e
13   
14   fg.map(List(Option("abc"),Option("xy"),Option("ryuiyty"))){ _.length }
15                                                   //> res5: List[Option[Int]] = List(Some(3), Some(2), Some(7))

map和map2都是正宗的于高阶数据类型结构外的函数施用,但flatMap的函数是
A=>M[B],会毁结果的组织。例如:我们对一个发出3个元素的List进行map操作,结果仍然是一个3单要素的List。但如若flatMap的话语虽可能会见起不同长短的List:

 在上述我们因此listFunctor处理了List[A]花色数据,optionFunctor处理Option[A]。最终我们之所以fg处理像List[Option[String]]种的数码。

既然是再次小心于函数施用,那么还发同种样式的函数是值得研究之:

 那么我们如果能够实现Monad[M[N]]的flatMap不就会博取此Monad实例了嘛:

1 def apply[A,B](fab: F[A => B])(fa: F[A]): F[B]

 

apply的应用函数是经过一个Monadic值传入的,这就是令apply比map更加强硬,因为这个用函数还带来在F结构的打算。就拿Option来说:apply的行使函数可以是None而map无论如何都必提供用函数。这样一来apply会比map更加灵敏和强硬。以下就是是Applicative
trait:

1   def composeM[M[_],N[_](m: Monad[M], n: Monad[N]): Monad[({type l[x] = M[N[x]]})#l]= {
2       new Monad[({type l[x] = M[N[x]]})#l] {
3           def flatMap[A,B](mna: M[N[A]])(f: A => M[N[B]]): M[N[B]] = {
4             ????? !!!!!
5           }
6       }
7   } 
 1   trait Applicative[F[_]] extends Functor[F] {
 2       def unit[A](a: A): F[A]
 3       def map2[A,B,C](fa: F[A], fb: F[B])(f: (A,B) => C): F[C] = {
 4           apply(fb)(map(fa)(f.curried))     //map(fa)(a => (b => c)) >>> F[A=>B]
 5       }
 6       def apply[A,B](fa: F[A])(fab: F[A =>B]): F[B] = {
 7  //         map2(fab,fa)((f,a) => f(a))
 8             map2(fab,fa)(_(_))
 9       }
10       def map[A,B](fa: F[A])(f: A => B): F[B] = {
11  //         map2(unit(f),fa)((f,a) => f(a))
12           map2(unit(f),fa)(_(_))
13       }
14       def mapByApply[A,B](fa: F[A])(f: A => B): F[B] = {
15           apply(fa)(unit(f))
16       }     
17   }

 

apply和map2可以并行实现。map可以用map2或apply来贯彻。所以Applicative
extends
Functor。下面我们来分析一下flatMap和map2的距离,这为意味着着Monad和Applicative在表现及的分别。我们因此Option来演示一下flatMap,map2及apply的个别:

伤心的是这次无法兑现flatMap。这个悲剧明确了想“Monad
do not
compose!”。那咱们的Monadic语言梦想就这么快磨了邪?实际上多只Monad定义的for-comprehension可以通过Monad
Transformer来实现。Monad
Transformer可以兑现多个Monad效果的长(stacking
effect)。好,那咱们就算开看这个Monad Transformer吧:

 1 def Map2[A,B,C](ma: Option[A], mb: Option[B])(f: (A,B) => C): Option[C] = {
 2       (ma,mb) match {
 3           case (Some(a),Some(b)) => Some(f(a,b))
 4           case _ => None
 5       }
 6   }
 7   def apply[A,B](ma: Option[A])(f: Option[A => B]): Option[B] = {
 8       (ma,f) match {
 9           case (Some(a),Some(f)) => Some(f(a))
10           case _ => None
11       }
12   }
13   def flatMap[A,B](ma: Option[A])(f: A => Option[B]): Option[B] = {
14       ma match {
15           case Some(a) => f(a)
16           case _ => None
17       }
18   }

咱俩先行实现一个Maybe
Monad:

apply和map的演算都依赖让片独传入参数的状态:只有区区个参数都是Some时才见面在Some结构中开展演算。而flatMap的散播函数A=>Option[B]是不是运行则借助让ma状态是不是Some,而流传函数运行的结果而赖让ma内元素A的值。所以我们规定Applicative可以保持运算结果的构造不变换,而Monad有或会见导致运算结果的布局变迁。

Maybe就是Option。由于scala标准库里已经生Option类型,为免函数引用混扰,所以定义一个初的Monad。

咱们解可以据此map2把有限独Monatic值M[A],M[B]使用函数(A+B)=>C连接起来,概括这个模式map3,map4,map5…都足以由至同一作用:

 1     trait Functor[F[_]] {
 2         def map[A,B](fa: F[A])(f: A => B): F[B]
 3     }
 4     trait Monad[M[_]] extends Functor[M] {
 5         def unit[A](a: A): M[A]
 6         def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B]
 7     }    
 8     trait Maybe[+A] {
 9         def map[B](f: A => B): Maybe[B] ={
10             this match {
11                 case Just(a) => Just(f(a))
12                 case _ => Nada
13             }
14         }
15         def flatMap[B](f: A => Maybe[B]): Maybe[B] = {
16             this match {
17                 case Just(a) => f(a)
18                 case _ => Nada
19             }
20         }
21     }
22     case class Just[+A](a: A) extends Maybe[A]
23     case object Nada extends Maybe[Nothing]

俺们早就为此map2实现了map3,map4,map5:

咱贯彻了Maybe类型的unit,map,flatMap,所以我们得以Maybe
Monad的条件里实现for-comprehension的用

 1     def map3[A,B,C,D](ma: M[A], mb: M[B], mc: M[C])(f: (A,B,C) => D): M[D] = {
 2         map2(ma, 
 3           map2(mb,mc){(b,c) => (b,c)}
 4           ){(a,bc) => {
 5            val (b,c) = bc
 6              f(a,b,c)
 7           }}
 8     }
 9     def map4[A,B,C,D,E](ma: M[A], mb: M[B], mc: M[C], md: M[D])(f: (A,B,C,D) => E): M[E] = {
10         map2(ma,
11           map2(mb,
12           map2(mc,md){(c,d) => (c,d)}
13           ){(b,cd) => (b,cd)}
14           ){(a,bcd) => {
15               val (b,(c,d)) = bcd
16               f(a,b,c,d)
17           }}
18     }
19     def map5[A,B,C,D,E,F](ma: M[A], mb: M[B], mc: M[C], md: M[D], me: M[E])(f: (A,B,C,D,E) => F): M[F] = {
20         map2(ma,
21           map2(mb,
22           map2(mc,
23           map2(md,me){(d,e) => (d,e)}
24           ){(c,de) => (c,de)}
25           ){(b,cde) => (b,cde)}
26           ){(a,bcde) => {
27               val (b,(c,(d,e))) = bcde
28               f(a,b,c,d,e)
29           }}
30     }

 

以上之兑现方式很标准:通过map2连续地对M[_]开展函数施用产生结果。用map2来展开函数施用相对比较复杂,那么因此特别的泛函施用apply会不会见重好把吗?让咱来尝试着推导一下:

1     val maybeFor: Maybe[Int] = for {
2         x <- Just(2)
3         y <- Just(5)
4         z = x * y
5     } yield z                                 //> maybeFor  : ch12.ex2.Maybe[Int] = Just(10)

先是我们可拿一个老三单符合参数的函数curry一下:f(A,B,C)
>>> A => B => C => D >>>
f.curried。然后拿函数放到unit里:

 

unit(f.curried) =
M[A=>B=>C]。apply(M[A])(M[A=>B]):M[B]。我们可以对每个M值分步施用apply:A=>B=>C
>>> A=>BC >>>
BC=B=>C,apply(M[A])(unit(f.curried))=M[B=>C],那么得用apply来实现map3,map4,map5:

俺们看到了一如既往段落嵌在for-comprehension内的行令运算。但运算的条件要求从表面上还无法肯定。那么,这段运算的旁一个本可能具有启示:

 1       def map3[A,B,C,D](ma: F[A], mb: F[B], mc: F[C])(f: (A,B,C) => D): F[D] = {
 2           apply(mc)(apply(mb)
 3               (apply(ma)(unit(f.curried))))
 4       }
 5       def map4[A,B,C,D,E](ma: F[A], mb: F[B], mc: F[C],md: F[D])(f: (A,B,C,D) => E): F[E] = {
 6           apply(md)(apply(mc)
 7           (apply(mb)
 8           (apply(ma)(unit(f.curried)))))
 9       }
10       def map5[A,B,C,D,E,G](ma: F[A], mb: F[B], mc: F[C],md: F[D], me: F[E])(f: (A,B,C,D,E) => G): F[G] = {
11           apply(me)(apply(md)
12           (apply(mc)
13           (apply(mb)
14           (apply(ma)(unit(f.curried))))))
15       }
1     val maybeMap: Maybe[Int] = {
2         Just(2).flatMap(x => Just(5).map(y => x * y))
3                                                   //> maybeMap  : ch12.ex2.Maybe[Int] = Just(10)
4     }

采取apply就理解很多了,我们唯有需要把一个函数进行curry后就此unit升格然后透过Monadic值把参数传进便可在泛函结构内运算函数了。

咱们掌握for-comprehension就是flatMap的方法糖。所以上述就是原始flatMap运算。从这个flatMap表达形式我们可以得出每一样句运算都得按主导Monad的flatMap函数类型(signature),也就是说类型必须配合。

因咱们可以就此flatMap来实现map2和apply,所以有Monad都是Applicative。由于我们于Monad组件库里已经实现广大中的组件函数,我们虽未待以Applicative库里再次了。我们可以针对Monad
extends Applicative:

咱再来一个熟悉的Monad,State
Monad:

 1  trait Monad[M[_]] extends Applicative[M]{
 2       def unit[A](a: A): M[A]
 3       def flatMap[A,B](ma: M[A])(f: A => M[B]): M[B] = {
 4           join(map(ma)(f))
 5       }
 6       def compose[A,B,C](f: A => M[B], g: B => M[C]): A => M[C] = {
 7           a => { flatMap(f(a))(g)}
 8       }
 9       def join[A](mma: M[M[A]]): M[A] = {
10           flatMap(mma)(ma => ma)
11       }
12        override def apply[A,B](ma: M[A])(fab: M[A => B]): M[B] = {
13           flatMap(fab)(f => flatMap(ma)(a => unit(f(a))))
14       }     

 

这样有Monad都好是Applicative。但是,有些Applicative未必是Monad,因为咱们也许无法用一点项目Applicative实例的map2或apply来贯彻flatMap、join、compose。

 1     type State[S,+A] = S => (A,S)
 2   object State {
 3       def getState[S]: State[S,S] = s => (s,s)
 4       def setState[S](s: S): State[S,Unit] = _ => ((),s)
 5   }
 6     class StateOps[S,A](sa: State[S,A]) {
 7         def unit(a: A) = (s: S) => (a,s)
 8         def map[B](f: A => B): State[S,B] = {
 9             s => {
10                 val (a,s1) = sa(s)
11                 (f(a),s1)
12             }
13         }
14         def flatMap[B](f: A => State[S,B]): State[S,B] = {
15             s => {
16                 val (a,s1) = sa(s)
17                 f(a)(s1)
18             }
19         }
20     def getState[S]: State[S,S] = s => (s,s)
21     def setState[S](s: S): State[S,Unit] = _ => ((),s)
22     }
23     implicit def toStateOps[S,A](sa: State[S,A]) = new StateOps(sa)
24                                                   //> toStateOps: [S, A](sa: ch12.ex2.State[S,A])ch12.ex2.StateOps[S,A]

咱俩或用一个事实上的例子来说明Monad与Applicative的不同行为:

 

一经我们设计一个网页的登陆页面,用户用填写name,birthdate,phone三单字段。提交页面后由于系统验证录入信息。我们前面使用了类型Either,刚好用来回到系统验证结果。系统要对三个字段展开验证,我们可以预先将当下三只验证函数的花样写出来:

同等我们好就此State
Monad定义的for-comprehension进行行令编程:

 1  implicit def eitherMonad[E] = new Monad[({type l[V] = Either[E,V]})#l] {
 2       def unit[A](a: A) = Right(a)
 3       def flatMap[A,B](ea: Either[E,A])(f: A => Either[E,B]): Either[E,B] = {
 4           ea match {
 5               case Right(a) => f(a)
 6               case Left(e) => Left(e)
 7           }
 8       }
 9   }
10   def validateName(name: String): Either[String,String]
11   def validatebirthdate(birthdate: Date): Either[String,Date]
12   def validatePhone(phone: String): Either[String,String]

 

即时三独说明函数都回去Either类型。因为起implict
eitherMonad实例所以可以flatMap验证函数的结果:

 1     import State._
 2     val stateFor: State[Int, Int] = for {
 3         x <- getState[Int]
 4         y = x * 5
 5         _ <- setState(x+1)
 6     } yield y                                 //> stateFor  : ch12.ex2.State[Int,Int] = <function1>
 7     
 8     
 9     stateFor(2)                               //> res0: (Int, Int) = (10,3)
10 
11 可以肯定这个State Monad for-comprehension内的行令运算同样需要遵循State Monad map, flatMap的类型匹配。
1 validateName(field1) flatMap (f1 =>
2 validateBirthdate(field2) flatMap (f2 =>
3 validatePhone(field3) map (WebForm(_, _, _))

 

WebForm是只构建函数(constructor):
case class WebForm(name: String, birthdate: Date, phone:
String)。如果我们像面那样逐个flatMap验证函数结果的讲话,从flatMap的现实性实现代码可以看来:如果validName返回错误的语句,下面的validateBirthdate,
validatePhone都未会见运作。系统直接以错误返回用户,用户只要先期改了第一独错误又付出后系统持续下一个字段的说明。如果急需填多单字段的消息表什么的饶再次凸显显麻烦了。如果我们用Applicative风格:

可肯定之State
Monad for-comprehension内的行令运算同样需按State Monad map,
flatMap的型匹配。

1 apply(apply(apply((WebForm(_, _, _)).curried)(
2   validateName(field1)))(
3   validateBirthdate(field2)))(
4   validatePhone(field3))

那么咱们下面将当下片单Monad在一个for-comprehension里运行。比如

采用apply三个证明函数之间就没其余借助和先后顺序。我们得外顺序来运行验证函数而且好包三个证函数都见面运作。我们从flatMap和apply不同的作为模式来证实Monad操作与Applicative操作是不尽相同的。

 1  val nocompileFor = {
 2       def remainder(a: Int, b: Int): Maybe[Int] = {
 3           a % b match {
 4               case 0 => Nada
 5               case r => Just(r)
 6           }
 7       }
 8       for {
 9           x <- getState[Int]    //State.flatMap
10           y <- remainder(x,2)   //Maybe.flatMap
11           z = x + y             //???.map
12           _ <- setState[Int](5) //State.flatMap
13       } yield y
14   }                                               

咱俩后续将此例子推进下:我们期望系统一次性运行有证函数。如果起同还是多个谬误就是以返回所有错误信息。由于可能要回到多修错误信息,Either类型已经不足以用了。我们摸索着加一个初的错误处理数据类型:

好拘留之出来,flatMap的花色且乱了仿照了。以上例子无法透过编译器。

1 trait Validation[+E,+A]
2 case class Failure[E](head: E, tail: Vector[E]) extends Validation[E,Nothing]
3 case class success[A](a: A) extends Validation[Nothing,A]

缓解方案:Monad
Transformer:

Validation类型的Failure可以包容多修数。注意
+E,+A使我们可代入Nothing:
Validate[E,Nothing],Validation[Nothing,A]。

点的失败例子是使解决State[Maybe[A]]这种类型的题目。我们尽管得一个State
Monad Transformer:

咱俩先看Applicative实例:

 1  import StateT._
 2     trait StateT[M[_],S,A] {   // State Monad Transformer
 3       def apply(s: S): M[(A,S)]
 4       
 5         def map[B](f: A => B)(implicit m: Functor[M]): StateT[M,S,B] = {
 6             stateT( s => m.map(apply(s)){
 7                 case (a,s1) => (f(a),s1)
 8             })
 9         }
10         
11         def flatMap[B](f: A => StateT[M,S,B])(implicit m: Monad[M]): StateT[M,S,B] = {
12             stateT( s => m.flatMap(apply(s)){
13                 case (a,s1) => f(a)(s1)
14             })
15         }
16         
17     }
18   object StateT {
19       def stateT[M[_],S,A](f: S => M[(A,S)]): StateT[M,S,A] = {
20           new StateT[M,S,A] {
21               def apply(s: S) = f(s)
22           }
23       }
24       def liftM[M[_],S,A](ma: M[A])(implicit m: Monad[M]): StateT[M,S,A] = {
25             stateT(s => m.map(ma)(a => (a, s)))
26       }
27   }
 1 implicit def validationApplicative[E] = new Applicative[({type l[A] = Validation[E,A]})#l] {
 2     def unit[A](a: A) = Success(a)
 3   def map2[A,B,C](fa: Validation[E,A], fb: Validation[E,B])(f: (A,B) => C): Validation[E,C] = {
 4     (fa,fb) match {
 5         case (Success(a),Success(b)) => Success(f(a,b))
 6         case (Failure(h1,t1),Failure(h2,t2)) => Failure(h1, t1 ++ Vector(h2) ++ t2)
 7         case (e@Failure(_,_),_) => e
 8         case (_,e@Failure(_,_)) => e
 9     }
10   }
11 }

 StateT是独State Monad
Transformer,同时StateT也是一个Monad实例,因为我们好实现它的flatMap函数。既然StateT是只Monad实例,那我们就是可以为此StateT来定义其的for-comprehension了:

map2+unit是Applicative的太中心组件函数。我们设实现即点儿独函数就尽了。

 

我们就就这个validateWebForm函数:

 1   val maybeState: StateT[Maybe,Int,Int] = {
 2     def getState[S]: StateT[Maybe,S,S] = stateT(s => Just((s,s)))
 3     def setState[S](s: S): StateT[Maybe,S,Unit] = stateT(s1 => Just(((),s)))
 4       def remainder(a: Int, b: Int): Maybe[Int] = {
 5           a % b match {
 6               case 0 => Nada
 7               case r => Just(r)
 8           }
 9       }
10       for {
11           x <- getState[Int]
12           y <- liftM[Maybe,Int,Int](remainder(x,2))
13           z = x + y
14           _ <- setState[Int](5)
15       } yield y
16   }                                               //> maybeState  : ch12.ex2.StateT[ch12.ex2.Maybe,Int,Int] = ch12.ex2$$anonfun$m
17                                                   //| ain$1$StateT$3$$anon$4@34b7bfc0
18  
19   maybeState(1)                                   //> res1: ch12.ex2.Maybe[(Int, Int)] = Just((1,5))
20   maybeState(0)                                   //> res2: ch12.ex2.Maybe[(Int, Int)] = Nada
 1 trait Validation[+E,+A]
 2 case class Failure[E](head: E, tail: Vector[E]) extends Validation[E,Nothing]
 3 case class Success[A](a: A) extends Validation[Nothing,A]
 4 implicit def validationApplicative[E] = new Applicative[({type l[A] = Validation[E,A]})#l] {
 5     def unit[A](a: A) = Success(a)
 6   def map2[A,B,C](fa: Validation[E,A], fb: Validation[E,B])(f: (A,B) => C): Validation[E,C] = {
 7     (fa,fb) match {
 8         case (Success(a),Success(b)) => Success(f(a,b))
 9         case (Failure(h1,t1),Failure(h2,t2)) => Failure(h1, t1 ++ Vector(h2) ++ t2)
10         case (e@Failure(_,_),_) => e
11         case (_,e@Failure(_,_)) => e
12     }
13   }
14 }
15 import java.util.Date  
16 case class WebForm(name: String, birthdate: Date, phone: String)
17 
18 def validateName(name: String): Validation[String, String] = {
19   if (name != "")
20        Success(name)
21   else Failure("Name cannot be empty", Vector())
22 }
23 
24 def validateBirthdate(birthdate: String): Validation[String, Date] = {
25   try {
26     import java.text._
27     Success((new SimpleDateFormat("yyyy-MM-dd")).parse(birthdate))
28   } catch {
29     case e => Failure("Birthdate must be in the form yyyy-MM-dd", Vector())
30   }
31 }
32 def validatePhone(phoneNumber: String): Validation[String, String] = {
33   if (phoneNumber.matches("[0-9]{10}"))
34        Success(phoneNumber)
35   else Failure("Phone number must be 10 digits", Vector())
36 }
37 def validateWebForm(name: String, birthdate: String, phone: String): Validation[String, WebForm] = {
38     apply(validateName(name))
39      (apply(validateBirthdate(birthdate))
40      (apply(validatePhone(phone))((WebForm(_,_,_)).curried)))))
41 }

 以上这for-comprehension是故StateT[Maybe,Int,Int]来定义的。那么所有以for-comprehension内之表达式右方就务须是StateT类型。上面的getState,setState函数结果还是StateT类型,但remainder函数返回结果却是Maybe类型。所以我们之所以liftM把Maybe类型升格到StateT类型。liftM的函数定义如下:

这虽是一个实实在在的Applicative应用例子。

1       def liftM[M[_],S,A](ma: M[A])(implicit m: Monad[M]): StateT[M,S,A] = {
2             stateT(s => m.map(ma)(a => (a, s)))
3       }

 

liftM的意就是是管一个Monad
M[A]晋升成为StateT。上面的例证我们为此liftM把Monad
Maybe升格成StateT类型,这样一切for-comprehension内部有表达式类型且相当了。注意StateT把State
Monad和其它其他一个Monad合起来用:上面的事例用了Maybe。实际上StateT[M,S,A]里之M可以是Maybe也可以是Option,Either,Validation。。。那我们就是得取得StateT[Option,Int,Int],StateT[Either,Int,Int]这些Monad
Transformer并在for-comprehension里体现这些组合Monad的机能。更要的凡StateT是独Monad那么我们得拿它们看做任何其他Monad一样跟任何Monad结合形成新的Monad
Transformer。

 

若是我们用处理反的种:Maybe[State],我们不怕用定义MaybeT。我们先行瞧MaybeT的路款式:

 

 caseclass MaybeT[M[_],A](run:
M[Maybe[A]]) 这是Monad Transformer通用款式

 

我们把共同使用的Monad包嵌在参数里:

 

 1     case class MaybeT[M[_],A](run: M[Maybe[A]]) {
 2         def map[B](f: A => B)(implicit m: Functor[M]): MaybeT[M,B] = {
 3             MaybeT[M,B](m.map(run)(a => a map f))
 4         }
 5         def flatMap[B](f: A => MaybeT[M,B])(implicit m: Monad[M]): MaybeT[M,B] = {
 6             MaybeT[M,B](m.flatMap(run) {
 7                 case Just(a) => f(a).run
 8                 case Nada => m.unit(Nada)
 9             })
10         }
11     }

 

而因此Option作为基本Monad,那么我们可设计一个Option的Monad
Transformer OptionT类型:

 

 1   case class OptionT[M[_],A](run: M[Option[A]]) {
 2       def map[B](f: A => B)(implicit m: Functor[M]): OptionT[M,B] = {
 3            OptionT[M,B](m.map(run)(a => a.map(f)))
 4       }
 5       def flatMap[B](f: A => OptionT[M,B])(implicit m: Monad[M]): OptionT[M,B] = {
 6           OptionT[M,B](m.flatMap(run) {
 7               case Some(a) => f(a).run
 8               case None => m.unit(None)
 9           })
10       }
11   }

 

无论如何,只要我们能将一头以的即简单个Monad升格成靶子Monad
Transformer类型格式就可放心在for-comprehension中进行行令编程了。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

相关文章