设为首页 加入收藏

TOP

Scalaz(11)- Monad:你存在的意义(一)
2017-10-10 12:13:35 】 浏览:6181
Tags:Scalaz Monad 存在 意义

    前面提到了scalaz是个函数式编程(FP)工具库。它提供了许多新的数据类型、拓展的标准类型及完整的一套typeclass来支持scala语言的函数式编程模式。我们知道:对于任何类型,我们只需要实现这个类型的typeclass实例就可以在对这个类型施用所对应typeclass提供的所有组件函数了(combinator)。突然之间我们的焦点好像都放在了如何获取typeclass实例上了,从而忽略了考虑为什么要使用这些typeclass及使用什么样的typeclass这些问题了。所以可能有人会问我:如何获取Int的Monad实例。我会反问:傻B,你疯了吗(are you insane)?你到底想干什么?这时傻B可能忽然会醒悟还没真正了解自己这样问的目的。看来我们还是回到问题的源头,从使用scalaz的基本目的开始考虑分析了。

    我们就围绕scalaz提供的我们都熟悉的typeclass Functor, Applicative, Monad来分析说明吧,因为我们在前面对它们都进行了讨论介绍,为了与scalaz提供的众多其它typeclass有所区分,我们就暂时把它们统称为Monadic typeclass吧。首先,这几个Monadic typeclass不是数据类型,而是代表着某些编程的模式。我们知道FP编程和OOP编程最大的区别就是FP编程的状态不可变性(immutability)、无副作用(no-side-effect)、纯函数组合能力(pure code composability),这就要求FP编程在某种壳子(context)里进行状态转变(transformation)。形象点表达就是F[T]。F[]就是各种独特的壳子(context)而T就是需要运算转变?的某种类型值。FP程序的结果形象描述就好像F[T] => F[U]: 代表在F[]壳子内对T进行运算,并把结果U保存在F[]内。既然FP编程相对于OOP编程是种全新的编程方式,那么自然需要一套新的程序状态转变方法,也就是一套新的操作函数施用模式了。Scalaz通过Functor, Applicative, Monad提供了三种基本的函数施用方式,它们都是针对F[T]里的T值:

1 // Functor : map[T,U] (F[T])(f: T => U): F[U] 2 // Applicative: ap[T,U] (F[T])(f: F[T => U]): F[U] 3 // Monad : flatMap[T,U](F[T])(f: T => F[U]): F[U]

以上函数施用方式产生同样的效果:F[T] => F[U],都是典型的FP编程方式。所以可以说Monadic typeclass提供了规范的FP编程框架(template),程序员可以使用这些框架进行FP编程。如果这样解释使用scalaz的目的,是不是更清楚一点了?

从另一个角度解释:scalaz typeclass 代表着抽象编程概念。typeclass是通过即兴多态来实现针对各种类型值的FP式计算的。回到开头傻B的问题:Int是一种基础类型,换句话说就是FP函数施用的目标。Monadic typeclass针对的类型是高阶的F[T]类型。我们需要对在F[]的作用环境里T类型值计算方式进行概括。我们真正需要获取的实例实际上是针对高阶类型F[_]的。所以傻B问了个错误的问题,肯定她当时不知自己在干什么。

现在我们可以分析一下应该使用什么typeclass了。总体来说,我的理解是可以把scalaz typeclass分成种类和特质:

种类定义了FP编程的各种模式。比如Functor, Applicative, Monad都代表不同的编程方式或者说它们都具备不同的程序运算模式。特质是指不同的数据类型所定义的typeclass实例控制着程序的具体运算行为。如Option Monad可以None状态中途终止运算、State Monad确保状态值一直随着程序运算。它们都因为基于不同类型的实例而表现不同的运算行为。Functor, Applicative, Monad的特质则由它们的实例中map, ap, flatMap这三个驱动函数的具体实现方式所决定。我们先看看现成的Option Functor,它的实现方式如下:

1 mplicit object optionFunctor extends Functor[Option] { 2     def map[T,U](ot: Option[T])(f: T => U): Option[U] = ot match { 3         case Some(t) => Some(f(t)) 4         case None => None 5  } 6 }

Option Functor实例驱动函数map的意思是说如果目标类型F[T]的值是个Some,那么我们就在Some壳内施用参数提供的一般函数f;如果目标值是None就不施用函数。我们再看看List Functor:

1 implicit object listFunctor extends Functor[List] { 2     def map[T,U](lt: List[T])(f: T => U): List[U] = lt match { 3         case Nil => Nil 4         case head :: tail => f(head) :: map(tail)(f) 5  } 6 }

List Functor的map函数彰显出对一串在壳内元素逐个转变的特性。从List操作方式就很容易理解:list.map(t => transform(t))

我们再看看Option Applicative的实例:

1 implicit object objectApplicative extends Applicative[Option] { 2     def point[T](t: T): Option[T] = Some(t) 3     def ap[T,U](ot: Option[T])(of: Option[T => U]): Option[U] = (ot, of) match { 4         case (Some(t), Some(f)) => Some(f(t)) 5         case _ => None 6  } 7 }

Option Applicative的驱动函数ap又一次凸显了Option的特别处理方式:只有在目标值和操作函数都不为None时才施用通过壳提供的操作函数。

再看看Option Monad实例:

1 mplicit object optionMonad extends Monad[Option] { 2     def flatMap[T,U](ot: Option[T])(f: T => Option[U]): Option[U] = ot match { 3         case Some(t) => f(t) 4         case _ => None 5  } 6 }

这个flatMap函数可以告诉我们更多东西:如果我们把Option[T]视作一个运算的话,那么只要这个运算结果不为None就可以选择连续运算,因为:f: T => Option[U],用文字描述即为给一个T值进行计算后产生另一个运算Option[U],如果再给Option[U]一个值进行计算的话就又会产生另一个运算Opton[V]... 如此持续:

F[A](a => F[B](b => F[C](c =

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scalaz(10)- Monad:就是一种.. 下一篇抓住大数据时代的红利

评论

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

最新文章

热门文章

C 语言

C++基础

windows编程基础

linux编程基础

C/C++面试题目