前面我们讨论了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]]

我们试试实现Map类型的sequence:

     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]]
}

这个trait里的sequence,traverse函数与我们前面实现的sequence和traverse有什么不同呢?

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)
}

raverse会保留Traverse类型的原始结构。这点从Traverse定律可以推导:针对Traverse[F], xs类型是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)

为什么需要这个context bound Applicative实例?实现traverse时需要Applicative的map,map2操作:

       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)(_ :: _))
}
}

现在我们通过Traverse类型可以实现游览(Traversal)那么Traverse和Foldable有什么区别吗?从表面上来看Traverse应该比Foldable更高效,因为Foldable是通过Monoid来对结构内的元素进行函数施用的,而Applicative比Monoid更强大。我们先看看Foldable最概括的操作函数foldMap:

def foldMap[A,B](as: F[A])(f: A => B)(mb: Monoid[B]): B

再看看traverse的函数款式:

def traverse[AP[_]: Applicative, A, B](ta: T[A])(f: A => AP[B]): AP[T[B]]

如果我们把AP[A]换成一个特制的类型:type ConstInt[A] = Int, 经过替换traverse就变成了:

def traverse[A,B](fa: T[A])(f: A => Int): Int

经过替换的traverse在函数款式上很像foldMap。那么我们就制造一个对任何B的类型:

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)
}
}

有了这个Applicative实例,我们就可以在trait Traverse里实现foldMap,也就意味着Traverse可以extend Foldable了:

    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))
}
}

既然Traverse已经概括了Foldable,而且Traverse类型有比Foldable效率高,那么以后尽量使用Traverse这种类型。

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
}

再用这个函数把T[A]转成List[A]:

     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
}

这两个利用State的函数语法十分相近。实际上所有State游览(traversal)都很相似。我们可以再进一步抽象:

    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
}

用mapS重新实现zipWithIndex,toList,foldLeft就简单的多。mapS游览天生是反序的,所以reverse函数只要对T[A]用mapS走一次就行了。

我们发现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

我们可以看到:tb的长度必须等于或大于ta。如此我们只有把这个函数分拆成两种情况的处理函数:

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

这样我们得出的T[(A,B)]其中T[A],T[B]短出的部分就用None来填补。

我们能像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))

两个Applicative实例通过product函数变成Applicative[(M,N)]实例。在traverse运行中m,n分别同时进行函数施用。

泛函编程(26)-泛函数据类型-Monad-Applicative Functor Traversal的更多相关文章

  1. 泛函编程(5)-数据结构(Functional Data Structures)

    编程即是编制对数据进行运算的过程.特殊的运算必须用特定的数据结构来支持有效运算.如果没有数据结构的支持,我们就只能为每条数据申明一个内存地址了,然后使用这些地址来操作这些数据,也就是我们熟悉的申明变量 ...

  2. 泛函编程(28)-粗俗浅解:Functor, Applicative, Monad

    经过了一段时间的泛函编程讨论,始终没能实实在在的明确到底泛函编程有什么区别和特点:我是指在现实编程的情况下所谓的泛函编程到底如何特别.我们已经习惯了传统的行令式编程(imperative progra ...

  3. 泛函编程(25)-泛函数据类型-Monad-Applicative

    上两期我们讨论了Monad.我们说Monad是个最有概括性(抽象性)的泛函数据类型,它可以覆盖绝大多数数据类型.任何数据类型只要能实现flatMap+unit这组Monad最基本组件函数就可以变成Mo ...

  4. 泛函编程(24)-泛函数据类型-Monad, monadic programming

    在上一节我们介绍了Monad.我们知道Monad是一个高度概括的抽象模型.好像创造Monad的目的是为了抽取各种数据类型的共性组件函数汇集成一套组件库从而避免重复编码.这些能对什么是Monad提供一个 ...

  5. 泛函编程(23)-泛函数据类型-Monad

    简单来说:Monad就是泛函编程中最概括通用的数据模型(高阶数据类型).它不但涵盖了所有基础类型(primitive types)的泛函行为及操作,而且任何高阶类或者自定义类一旦具备Monad特性就可 ...

  6. 备份-泛函编程(23)-泛函数据类型-Monad

    泛函编程(23)-泛函数据类型-Monad http://www.cnblogs.com/tiger-xc/p/4461807.html https://blog.csdn.net/samsai100 ...

  7. 泛函编程(27)-泛函编程模式-Monad Transformer

    经过了一段时间的学习,我们了解了一系列泛函数据类型.我们知道,在所有编程语言中,数据类型是支持软件编程的基础.同样,泛函数据类型Foldable,Monoid,Functor,Applicative, ...

  8. 泛函编程(32)-泛函IO:IO Monad

    由于泛函编程非常重视函数组合(function composition),任何带有副作用(side effect)的函数都无法实现函数组合,所以必须把包含外界影响(effectful)副作用不纯代码( ...

  9. 泛函编程(34)-泛函变量:处理状态转变-ST Monad

    泛函编程的核心模式就是函数组合(compositionality).实现函数组合的必要条件之一就是参与组合的各方程序都必须是纯代码的(pure code).所谓纯代码就是程序中的所有表达式都必须是Re ...

随机推荐

  1. scp命令[转]

    scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度.当你服务器 ...

  2. Atitit usrqbg1821 Tls 线程本地存储(ThreadLocal Storage 规范标准化草案解决方案ThreadStatic

    Atitit usrqbg1821 Tls 线程本地存储(ThreadLocal Storage 规范标准化草案解决方案ThreadStatic 1.1. ThreadLocal 设计模式1 1.2. ...

  3. Atitit 图像处理 公共模块 矩阵扫描器

    Atitit 图像处理 公共模块 矩阵扫描器 1.1. 调用说明对矩阵像素遍历处理调用1 2. 矩阵扫描器主题结构1 2.1. 主要说明 从像素点开始填充矩阵1 2.2. 得到模板中心点所对应的图像坐 ...

  4. atitit。企业的价值观 员工第一 vs 客户第一.docx

    atitit.企业的价值观 员工第一 vs 客户第一.docx 1. 客户第一的说法是错误的,员工优先是正确的,理念与价值观1 1.1. 任何一个组织,应该组织成员优先级要比外部成员高才对1 1.2. ...

  5. Android限定EditText的输入类型为数字或者英文(包括大小写),EditText,TextView只能输入两位小数

    Android限定EditText的输入类型为数字或者英文(包括大小写) // 监听密码输入框的输入内容类型,不可以输入中文    TextWatcher mTextWatcher = new Tex ...

  6. SQL Pass北京举办第10次线下活动,欢迎报名

    活动主题: 探讨真实世界中的复制(第二季)与Windows Azure SQL Database内幕 地点:北京微软(中国)有限公司[望京利星行],三层308室 时间:2013年 9 月28日 13: ...

  7. JS之模板技术(aui / artTemplate)

    artTemplate是个好东西啊,一个开源的js前端模板引擎,使用简单,渲染效率特别的高. 我经常使用这个技术来在前端动态生成新闻列表,排行榜,历史记录等需要在前端列表显示的信息. 下面是artTe ...

  8. hibernate(五)核心开发接口与对象的三种状态

    本文链接:http://www.orlion.ml/37/ 一.Configuration 1.AnnotationConfiguration 2.进行配置信息的管理 3.configure()方法通 ...

  9. android 键盘遮挡 父布局上滚解决

    private void controlKeyboardLayout(final View root, final View scrollToView) { root.getViewTreeObser ...

  10. nodejs图片转换字节保存

    fs.readFile('上传图片路径', function(err, data) { if (err) { // 错误处理 return; } var base64str = new Buffer( ...