前面我们讨论了Applicative。Applicative 就是某种Functor,因为我们可以用map2来实现map,所以Applicative可以map,就是Functor,叫做Applicative Functor。我们又说所有Monad都是Applicative,因为我们可以用flatMap来实现map2,但不是所有数据类型的flatMap都可以用map2实现,所以反之不是所有Applicative都是Monad。Applicative注重于各种类型的函数施用,也就是map。包括普通函数的施用及在高阶类型结构内的函数施用,还有多参数函数的连续施用。表面上看来Monad已经覆盖了Functor, Applicative。可能就是因为Monad的功能太强大了,所以Monad的函数组合(functional composition)非常有限。我们只能从Applicative来获取函数组合这部分的能力了。
我们先研究一下Applicative的一些规则:
1、map的恒等定律:map(v)(x => x) === v
map(v)(f) === apply(unit(f))(v), 就是说把一个函数f用unit升阶后apply就是map。apply就是map的一种。
Applicative的恒等定律:apply(unit(x => x))(v) === v
2、Applicative的函数组合:如果我们具备以下条件:
f类型 = F[A => B], g类型 = F[B => C],x类型 = F[A]。那么:
apply(map2(f,g)(_ compose _))(x) === apply(f)(apply(g)(x)),或者更直接一点:
map3(f,g,x)(_(_(_))) === f(g(x))。map3就是函数升阶组合:把函数 (_(_(_))) >>> (a,b,c) => d 在高阶类型结构内进行组合。
先看看两个Applicative组件的实现:
def product[F[_],G[_]](a1: Applicative[F],a2: Applicative[G]) = new Applicative[({type l[V] = (F[V], G[V])})#l] { def unit[A](a: A) = (a1.unit(a), a2.unit(a)) override def apply[A,B](aa: (F[A],G[A]))(fg: (F[A => B], G[A => B])) = { (a1.apply(aa._1)(fg._1), a2.apply(aa._2)(fg._2)) } override def map2[A,B,C](fga: (F[A],G[A]), fgb: (F[B],G[B]))(f: (A,B) => C): (F[C],G[C]) = { (a1.map2(fga._1,fgb._1)(f), a2.map2(fga._2,fgb._2)(f)) } } //> product: [F[_], G[_]](a1: ch12.ex1.Applicative[F], a2: ch12.ex1.Applicative //| [G])ch12.ex1.Applicative[[V](F[V], G[V])] def compose[F[_],G[_]](a1: Applicative[F], a2: Applicative[G]) = new Applicative[({type l[V] = F[G[V]]})#l] { def unit[A](a: A) = a1.unit(a2.unit(a)) override def map2[A,B,C](fga: F[G[A]], fgb: F[G[B]])(f: (A,B) => C): F[G[C]] = { a1.map2(fga,fgb)((ga,gb) => a2.map2(ga,gb)(f)) } } //> compose: [F[_], G[_]](a1: ch12.ex1.Applicative[F], a2: ch12.ex1.Applicative //| [G])ch12.ex1.Applicative[[V]F[G[V]]]
对于Applicative F,G来说 (F[A],G[A]), F[G[A]]也都是Applicative。通过对两个Applicative进行函数组合后形成一个更高阶的Applicative。这个Applicative和其它普通的Applicative一样可以实现多参数函数的升阶连续施用。
Applicative另外一个强项体现在针对可游览类型(traversable type)内部元素的函数施用(map)。与可折叠类型(foldable type)相比较,traversable类型更加抽象,能覆盖类型更多数据类型。Foldable类型的map用Monoid实现,但Monoid无法完全实现Traversable类型的map。Traversable类型的map是通过Applicative实现。Traversable覆盖了Foldable,所以Foldable只是Traversable的其中一种案例。那么我们就看看这个Traversable类型:
在前面我们讨论过的数据类型里,我们都会实现traverse,sequence这两个函数。那是因为我们尝试把那些数据类型都变成Traversable类型。traverse,sequence的函数款式是这样的:
def traverse[F[_],A,B](as: List[A], f: A => F[B]): F[List[B]] def sequence[F[_],A](fas: List[F[A]]): F[List[A]]
def sequenceMap[K,V](mfv: Map[K,F[V]]): F[Map[K,V]] = { mfv.foldLeft(unit(Map[K,V]())) { case (fm,(k,fv)) => map2(fm, map(fv)(v => Map(k -> v)))((m,n) => m ++ n) } }
实现过程还是挺复杂的。这里有些特别的地方需要注意:在实现Applicative实例时最好实现map2,因为它的函数款式更简单清晰。而在进行Applicative操作时使用apply会更方便。
既然Traversable是那么地普遍,为什么不把它抽象出来形成一个特殊的类型呢?
trait Traverse[T[_]] { def sequence[AP[_]: Applicative, A](tapa: T[AP[A]]): AP[T[A]] def traverse[AP[_]: Applicative, A, B](ta: T[A])(f: A => AP[B]): AP[T[B]] }
F[_]变成了AP[_]:Applicative:就是说AP必须是一个Applicative
List变成了T:trait Traverse针对任何T[_],包括List,更概括了。以前的sequence和traverse都只针对List,现在的Traverse类型可以拓展概括所有T[_]这种类型。
我们试着实现这个Traverse类型:
trait Traverse[T[_]] { def sequence[AP[_]: Applicative, A](tapa: T[AP[A]]): AP[T[A]] = traverse(tapa)(apa =>apa) def traverse[AP[_]: Applicative, A, B](ta: T[A])(f: A => AP[B]): AP[T[B]] = sequence(map(ta)(f)) def map[A,B](ta: T[A])(f: A => B): T[B] }
我们可以试着实现List,Option,Tree这几个Traverse类型实例:
case class Tree[+A](head: A, tail: List[Tree[A]]) object Traverse { val listTraverse = new Traverse[List] { override def traverse[AP[_],A,B](la: List[A])(f: A => AP[B])(implicit m: Applicative[AP]): AP[List[B]] = { la.foldLeft(m.unit(List[B]()))((a,b) => m.map2(f(b),a)(_ :: _)) } } val optionTraverse = new Traverse[Option] { override def traverse[AP[_],A,B](oa: Option[A])(f: A => AP[B])(implicit m: Applicative[AP]): AP[Option[B]] = { oa match { case Some(a) => m.map(f(a))(b => Some(b)) case None => m.unit(None) } } } val treeTraverse = new Traverse[Tree] { override def traverse[AP[_],A,B](ta: Tree[A])(f: A => AP[B])(implicit m: Applicative[AP]): AP[Tree[B]] = { m.map2(f(ta.head),listTraverse.traverse(ta.tail)(da => traverse(da)(f)))(Tree(_,_)) } }
所有Traverse类型的实例只要实现traverse或sequence就可以了,因为traverse和sequence相互可以实现。
sequence和traverse可以相互实现,但sequence的实现需要使用map。我们可以试着在trait Traverse里实现一个默认的map函数:
我们可以得到一个Identity Functor:type Id[A] = A, 这个东西存粹是为了获取F[A]这么个形状以便匹配类型款式。这样我们可以得出一个Identity Monad:
type Id[A] = A val idMonad = new Monad[Id] { def unit[A](a: A) = a // override def flatMap[A,B](a: Id[A])(f: A => Id[B]): Id[B] = f(a) override def flatMap[A,B](a: A)(f: A => B): B = f(a) }
traverse[Id,A,A](xs)(x => x) === xs >>> map(xs)(x => x) ===xs, 这不就是Functor恒等定律吗?也就是说把traverse需要的Applicative Functor降至Id Functor后traverse相当于map操作。换句话说Traverse可以概括Functor并且traverse操作要比map操作强大许多。
这样我们用idMonad就可以实现一个默认的map函数:
trait Traverse[T[_]] extends Functor[T] { def sequence[AP[_]: Applicative, A](tapa: T[AP[A]]): AP[T[A]] = traverse(tapa)(apa =>apa) def traverse[AP[_]: Applicative, A, B](ta: T[A])(f: A => AP[B]): AP[T[B]] = sequence(map(ta)(f)) def map[A,B](ta: T[A])(f: A => B): T[B] = traverse[Id,A,B](ta)(f)(idMonad) type Id[A] = A val idMonad = new Monad[Id] { def unit[A](a: A) = a // override def flatMap[A,B](a: Id[A])(f: A => Id[B]): Id[B] = f(a) override def flatMap[A,B](a: A)(f: A => B): B = f(a) } }
注意:在scala语法中:
def traverse[AP[_]: Applicative, A, B](ta: T[A])(f: A => AP[B]): AP[T[B]]AP[_]:Applicative是context bound, 相当于:
def traverse[AP[_], A, B](ta: T[A])(f: A => AP[B])(implicit m: Applicative[AP]): AP[T[B]]所以:
def map[A,B](ta: T[A])(f: A => B): T[B] = traverse[Id,A,B](ta)(f)(idMonad)
val listTraverse = new Traverse[List] { override def traverse[AP[_],A,B](la: List[A])(f: A => AP[B])(implicit m: Applicative[AP]): AP[List[B]] = { la.foldLeft(m.unit(List[B]()))((a,b) => m.map2(f(b),a)(_ :: _)) } }
def foldMap[A,B](as: F[A])(f: A => B)(mb: Monoid[B]): B
def traverse[AP[_]: Applicative, A, B](ta: T[A])(f: A => AP[B]): AP[T[B]]
def traverse[A,B](fa: T[A])(f: A => Int): Int
type Const[A,B] = A
用这个类型加上Monoid实现一个Applicative实例:
object Applicative { type Const[A, B] = A implicit def monoidApplicative[M](m: Monoid[M]) = new Applicative[({type alias[x] = Const[M,x]})#alias] { def unit[A](a: A): M = m.zero override def apply[A,B](m1: M)(m2: M): M = m.op(m1, m2) // override def map2[A,B,C](m1: M, m2: M)(f: (A,B) => C): M = m.op(m1, m2) } }
trait Traverse[T[_]] extends Functor[T] with Foldable[T] { def sequence[AP[_]: Applicative, A](tapa: T[AP[A]]): AP[T[A]] = traverse(tapa)(apa => apa) def traverse[AP[_]: Applicative, A, B](ta: T[A])(f: A => AP[B]): AP[T[B]] = sequence(map(ta)(f)) def map[A, B](ta: T[A])(f: A => B): T[B] = traverse[Id, A, B](ta)(f)(idMonad) type Id[A] = A val idMonad = new Monad[Id] { def unit[A](a: A) = a // override def flatMap[A,B](a: Id[A])(f: A => Id[B]): Id[B] = f(a) override def flatMap[A, B](a: A)(f: A => B): B = f(a) } import Applicative._ override def foldMap[A,B](ta: T[A])(f: A => B)(mb: Monoid[B]): B = { traverse[({type alias[x] = Const[B,x]})#alias,A,Nothing](ta)(f)(monoidApplicative(mb)) } }
State能够很巧妙地对高阶数据类型结构内部元素进行函数施用同时又维护了运算状态数据。前面我们已经取得了State Nonad实例。因为所有Monad都是Applicative,所以等于我们已经获取了State Applicative Functor实例。如果我们在游览(traverse)一个集合的过程中用State Applicative Functor对集合元素进行操作并且维护状态数据,那么将会实现强大的高阶数据类型处理功能。我们先看一个结合State的traverse函数:
def traverseS[S,A,B](ta: T[A])(f: A => State[S,B]): State[S,T[B]] = { traverse[({type alias[x] = State[S,x]})#alias, A, B](ta)(f)(StateMonad) }
def zipWithIndex[A](ta: T[A]): T[(A,Int)] = { traverseS(ta)(a => for { i <- getState _ <- setState(i + 1) } yield(a,i) )).run(0)._1 }
def toList[A](ta: T[A]): List[A] = { traverseS(ta)(a => for { as <- getState[List[A]] _ <- setState(a :: as) } yield() )).run(List[A]())._2.reverse }
def mapS[S,A,B](ta: T[A], s: S)(f: (A,S) => (B,S)): (T[B],S) = { traverse(ta)(a => for { s1 <- getState (b,s2) = f(a,s1) _ <- setState(s2) } yield(b) )).run(s) } def zipWithIndex[A](ta: T[A]): T[(A,Int)] = { mapS(ta,0)((a,s) => ((a,s),s+1))._1 } def toList[A](ta: T[A]): List[A] = { mapS(ta,List[A]())((a,s) => ((), a :: s)).reverse } def reverse[A](ta: T[A]): T[A] = { mapS(ta,toList(ta))((_,s) => (s.head, s.tail))._1 } def foldLeft[A,B](ta: T[A])(z: B)(f: (B,A) => B): B = { mapS(ta,z)((a,b) => ((),f(b,a)))._2 }
我们发现Traverse类型会保持它的结构,这是它的强项也是它的弱点。如果我们尝试将两个Traverse结构T[A],T[B]拼接成T[(A,B}]时就会发现这个操作对T[A]和T[B]的长度是有一定要求的。我们先试着用mapS来拼接T[A],T[B]:
def zip[A,B](ta: T[A], tb: T[B]): T[(A, B)] = (mapS(ta, toList(tb)) { case (a, Nil) => sys.error("zip: Incompatible shapes.") case (a, b :: bs) => ((a, b), bs) })._1
1、ta 长度大于 tb : 用下面的zipL函数
2、tb 长度大于 ta : 用下面的zipR函数
def zipL[A,B](ta: T[A], tb: T[B]): T[(A, Option[B])] = (mapS(ta, toList(tb)) { case (a, Nil) => ((a, None), Nil) case (a, b :: bs) => ((a, Some(b)), bs) })._1 def zipR[A,B](ta: T[A], tb: T[B]): T[(Option[A], B)] = (mapS(tb, toList(ta)) { case (b, Nil) => ((None, b), Nil) case (b, a :: as) => ((Some(a), b), as) })._1
我们能像Monoid product 一样在对一个可折叠结构进行游览时对结构内部元素一次性进行多次操作,我们同样可以对可游览结构(Traversable)在一轮游览时对结构内部元素进行多次操作:
def fuse[M[_],N[_],A,B](ta: T[A])(f: A => M[B], g: A => N[B]) (implicit m: Applicative[M], n: Applicative[N]): (M[T[B]], N[T[B]]) = traverse[({type f[x] = (M[x], N[x])})#f, A, B](ta)(a => (f(a), g(a)))(product(m, n))