Scalaz(8)- typeclass:Monoid and Foldable

Monoid是种最简单的typeclass类型。我们先看看scalaz的Monoid typeclass定义:scalaz/Monoid.scala

1 trait Monoid[F] extends Semigroup[F] { self =>
2   ////
3   /** The identity element for `append`. */
4   def zero: F
5 ...

Monoid trait又继承了Semigroup:scalaz/Semigroup.scala

 1 trait Semigroup[F]  { self =>
 2   ////
 3   /**
 4    * The binary operation to combine `f1` and `f2`.
 5    *
 6    * Implementations should not evaluate the by-name parameter `f2` if result
 7    * can be determined by `f1`.
 8    */
 9   def append(f1: F, f2: => F): F
10 ...

所以获取一个类型的Monoid实例需要实现zero和append这两个抽象函数。实际上Monoid typeclass也就是支持了append(|+|)这么一个简单的操作。scalaz为一些标准类型定义了Monoid实例:

1 0 |+| 30                                         //> res0: Int = 50
2 20.some |+| 30.some                               //> res1: Option[Int] = Some(50)
3 List(1,2,3) |+| List(4,5,6)                       //> res2: List[Int] = List(1, 2, 3, 4, 5, 6)
4 Tags.Multiplication(3) |+| Monoid[Int @@ Tags.Multiplication].zero
5                                                   //> res3: [email protected]@[Int,scalaz.Tags.Multiplication] = 3
6 Tags.Conjunction(true) |+| Tags.Conjunction(false)//> res4: [email protected]@[Boolean,scalaz.Tags.Conjunction] = false
7 Tags.Disjunction(true) |+| Tags.Disjunction(false)//> res5: [email protected]@[Boolean,scalaz.Tags.Disjunction] = true
8 Monoid[Boolean @@ Tags.Conjunction].zero          //> res6: [email protected]@[Boolean,scalaz.Tags.Conjunction] = true
9 Monoid[Boolean @@ Tags.Disjunction].zero          //> res7: [email protected]@[Boolean,scalaz.Tags.Disjunction] = false

就这么来看好像没什么值得提的。不过Ordering的Monoid倒是值得研究一下。我们先看看Ordering trait:scalaz/Ordering.scala

 1  implicit val orderingInstance: Enum[Ordering] with Show[Ordering] with Monoid[Ordering] = new Enum[Ordering] with Show[Ordering] with Monoid[Ordering] {
 2     def order(a1: Ordering, a2: Ordering): Ordering = (a1, a2) match {
 3       case (LT, LT)      => EQ
 4       case (LT, EQ | GT) => LT
 5       case (EQ, LT)      => GT
 6       case (EQ, EQ)      => EQ
 7       case (EQ, GT)      => LT
 8       case (GT, LT | EQ) => GT
 9       case (GT, GT)      => EQ
10     }
11
12     override def shows(f: Ordering) = f.name
13
14     def append(f1: Ordering, f2: => Ordering): Ordering = f1 match {
15       case Ordering.EQ => f2
16       case o           => o
17     }
18 ...

这里定义了Ordering的Monoid实例。它的append函数意思是:两个Ordering类型值f1,f2的append操作结果:假如f1是EQ就是f2,否则是f1:

 1 (Ordering.EQ: Ordering) |+| (Ordering.GT: Ordering)
 2                                                   //> res8: scalaz.Ordering = GT
 3 (Ordering.EQ: Ordering) |+| (Ordering.LT: Ordering)
 4                                                   //> res9: scalaz.Ordering = LT
 5 (Ordering.GT: Ordering) |+| (Ordering.EQ: Ordering)
 6                                                   //> res10: scalaz.Ordering = GT
 7 (Ordering.LT: Ordering) |+| (Ordering.EQ: Ordering)
 8                                                   //> res11: scalaz.Ordering = LT
 9 (Ordering.LT: Ordering) |+| (Ordering.GT: Ordering)
10                                                   //> res12: scalaz.Ordering = LT
11 (Ordering.GT: Ordering) |+| (Ordering.LT: Ordering)
12                                                   //> res13: scalaz.Ordering = GT

如果我用以上的特性来比较两个String的长度:如果长度相等则再比较两个String的字符顺序。这个要求刚好符合了Ordering Monoid实例的append操作:

1 3 ?|? 4                                           //> res14: scalaz.Ordering = LT
2 "abc" ?|? "bac"                                   //> res15: scalaz.Ordering = LT
3 def strlenCompare(lhs: String, rhs: String): Ordering =
4  (lhs.length ?|? rhs.length) |+| (lhs ?|? rhs)    //> strlenCompare: (lhs: String, rhs: String)scalaz.Ordering
5
6 strlenCompare("abc","aabc")                       //> res16: scalaz.Ordering = LT
7 strlenCompare("abd","abc")                        //> res17: scalaz.Ordering = GT

这个示范倒是挺新鲜的。

好了,单看Monoid操作会觉着没什么特别,好像不值得研究。实际上Monoid的主要用途是在配合可折叠数据结构(Foldable)对结构内部元素进行操作时使用的。我们再看看这个Foldable typeclass:scalaz/Foldable.scala

 1 trait Foldable[F[_]]  { self =>
 2   ////
 3   import collection.generic.CanBuildFrom
 4   import collection.immutable.IndexedSeq
 5
 6   /** Map each element of the structure to a [[scalaz.Monoid]], and combine the results. */
 7   def foldMap[A,B](fa: F[A])(f: A => B)(implicit F: Monoid[B]): B
 8   /** As `foldMap` but returning `None` if the foldable is empty and `Some` otherwise */
 9   def foldMap1Opt[A,B](fa: F[A])(f: A => B)(implicit F: Semigroup[B]): Option[B] = {
10     import std.option._
11     foldMap(fa)(x => some(f(x)))
12   }
13
14   /**Right-associative fold of a structure. */
15   def foldRight[A, B](fa: F[A], z: => B)(f: (A, => B) => B): B
16 ...

Foldable typeclass提供了许多注入方法支持折叠操作: scalaz/syntax/FoldableSyntax.scala

 1 final class FoldableOps[F[_],A] private[syntax](val self: F[A])(implicit val F: Foldable[F]) extends Ops[F[A]] {
 2   ////
 3   import collection.generic.CanBuildFrom
 4   import Leibniz.===
 5   import Liskov.<~<
 6
 7   final def foldMap[B: Monoid](f: A => B = (a: A) => a): B = F.foldMap(self)(f)
 8   final def foldMap1Opt[B: Semigroup](f: A => B = (a: A) => a): Option[B] = F.foldMap1Opt(self)(f)
 9   final def foldRight[B](z: => B)(f: (A, => B) => B): B = F.foldRight(self, z)(f)
10   final def foldMapRight1Opt[B](z: A => B)(f: (A, => B) => B): Option[B] = F.foldMapRight1Opt(self)(z)(f)
11   final def foldRight1Opt(f: (A, => A) => A): Option[A] = F.foldRight1Opt(self)(f)
12   final def foldLeft[B](z: B)(f: (B, A) => B): B = F.foldLeft(self, z)(f)
13   final def foldMapLeft1Opt[B](z: A => B)(f: (B, A) => B): Option[B] = F.foldMapLeft1Opt(self)(z)(f)
14   final def foldLeft1Opt(f: (A, A) => A): Option[A] = F.foldLeft1Opt(self)(f)
15   final def foldRightM[G[_], B](z: => B)(f: (A, => B) => G[B])(implicit M: Monad[G]): G[B] = F.foldRightM(self, z)(f)
16   final def foldLeftM[G[_], B](z: B)(f: (B, A) => G[B])(implicit M: Monad[G]): G[B] = F.foldLeftM(self, z)(f)
17   final def foldMapM[G[_] : Monad, B : Monoid](f: A => G[B]): G[B] = F.foldMapM(self)(f)
18   final def fold(implicit A: Monoid[A]): A = F.fold(self)(A)
19   final def foldr[B](z: => B)(f: A => (=> B) => B): B = F.foldr(self, z)(f)
20   final def foldr1Opt(f: A => (=> A) => A): Option[A] = F.foldr1Opt(self)(f)
21   final def foldl[B](z: B)(f: B => A => B): B = F.foldl(self, z)(f)
22   final def foldl1Opt(f: A => A => A): Option[A] = F.foldl1Opt(self)(f)
23   final def foldrM[G[_], B](z: => B)(f: A => ( => B) => G[B])(implicit M: Monad[G]): G[B] = F.foldrM(self, z)(f)
24   final def foldlM[G[_], B](z: B)(f: B => A => G[B])(implicit M: Monad[G]): G[B] = F.foldlM(self, z)(f)
25   final def length: Int = F.length(self)
26   final def index(n: Int): Option[A] = F.index(self, n)
27   final def indexOr(default: => A, n: Int): A = F.indexOr(self, default, n)
28   final def sumr(implicit A: Monoid[A]): A = F.foldRight(self, A.zero)(A.append)
29   final def suml(implicit A: Monoid[A]): A = F.foldLeft(self, A.zero)(A.append(_, _))
30   final def toList: List[A] = F.toList(self)
31   final def toVector: Vector[A] = F.toVector(self)
32   final def toSet: Set[A] = F.toSet(self)
33   final def toStream: Stream[A] = F.toStream(self)
34   final def toIList: IList[A] = F.toIList(self)
35   final def toEphemeralStream: EphemeralStream[A] = F.toEphemeralStream(self)
36   final def to[G[_]](implicit c: CanBuildFrom[Nothing, A, G[A]]) = F.to[A, G](self)
37   final def all(p: A => Boolean): Boolean = F.all(self)(p)
38   final def ∀(p: A => Boolean): Boolean = F.all(self)(p)
39   final def allM[G[_]: Monad](p: A => G[Boolean]): G[Boolean] = F.allM(self)(p)
40   final def anyM[G[_]: Monad](p: A => G[Boolean]): G[Boolean] = F.anyM(self)(p)
41   final def any(p: A => Boolean): Boolean = F.any(self)(p)
42   final def ∃(p: A => Boolean): Boolean = F.any(self)(p)
43   final def count: Int = F.count(self)
44   final def maximum(implicit A: Order[A]): Option[A] = F.maximum(self)
45   final def maximumOf[B: Order](f: A => B): Option[B] = F.maximumOf(self)(f)
46   final def maximumBy[B: Order](f: A => B): Option[A] = F.maximumBy(self)(f)
47   final def minimum(implicit A: Order[A]): Option[A] = F.minimum(self)
48   final def minimumOf[B: Order](f: A => B): Option[B] = F.minimumOf(self)(f)
49   final def minimumBy[B: Order](f: A => B): Option[A] = F.minimumBy(self)(f)
50   final def longDigits(implicit d: A <:< Digit): Long = F.longDigits(self)
51   final def empty: Boolean = F.empty(self)
52   final def element(a: A)(implicit A: Equal[A]): Boolean = F.element(self, a)
53   final def splitWith(p: A => Boolean): List[NonEmptyList[A]] = F.splitWith(self)(p)
54   final def selectSplit(p: A => Boolean): List[NonEmptyList[A]] = F.selectSplit(self)(p)
55   final def collapse[X[_]](implicit A: ApplicativePlus[X]): X[A] = F.collapse(self)
56   final def concatenate(implicit A: Monoid[A]): A = F.fold(self)
57   final def intercalate(a: A)(implicit A: Monoid[A]): A = F.intercalate(self, a)
58   final def traverse_[M[_]:Applicative](f: A => M[Unit]): M[Unit] = F.traverse_(self)(f)
59   final def traverseU_[GB](f: A => GB)(implicit G: Unapply[Applicative, GB]): G.M[Unit] =
60     F.traverseU_[A, GB](self)(f)(G)
61   final def traverseS_[S, B](f: A => State[S, B]): State[S, Unit] = F.traverseS_(self)(f)
62   final def sequence_[G[_], B](implicit ev: A === G[B], G: Applicative[G]): G[Unit] = F.sequence_(ev.subst[F](self))(G)
63   final def sequenceS_[S, B](implicit ev: A === State[S,B]): State[S,Unit] = F.sequenceS_(ev.subst[F](self))
64   def sequenceF_[M[_],B](implicit ev: F[A] <~< F[Free[M,B]]): Free[M, Unit] = F.sequenceF_(ev(self))
65   final def msuml[G[_], B](implicit ev: A === G[B], G: PlusEmpty[G]): G[B] = F.foldLeft(ev.subst[F](self), G.empty[B])(G.plus[B](_, _))
66   ////
67 }

这简直就是一个完整的函数库嘛。scalaz为大多数标准库中的集合类型提供了Foldable实例,也就是说大多数scala集合类型都支持这么一堆折叠操作函数。我还看不到任何需要去自定义集合类型,标准库的集合类型加上Foldable typeclass应该足够用了。

在Foldable typeclass中比较重要的函数就是foldMap了:

1 trait Foldable[F[_]]  { self =>
2   ////
3   import collection.generic.CanBuildFrom
4   import collection.immutable.IndexedSeq
5
6   /** Map each element of the structure to a [[scalaz.Monoid]], and combine the results. */
7   def foldMap[A,B](fa: F[A])(f: A => B)(implicit F: Monoid[B]): B

首先,foldMap需要Monoid[B]实例来实现。用List来举例:List trait 继承了Traverse:scalaz/std/List.scala

1 trait ListInstances extends ListInstances0 {
2   implicit val listInstance = new Traverse[List] with MonadPlus[List] with Zip[List] with Unzip[List] with Align[List] with IsEmpty[List] with Cobind[List] {
3 ...

在Traverse typeclass里定义了Foldable实例:scalaz/Traverse.scala

 1  def foldLShape[A,B](fa: F[A], z: B)(f: (B,A) => B): (B, F[Unit]) =
 2     runTraverseS(fa, z)(a => State.modify(f(_, a)))
 3
 4   override def foldLeft[A,B](fa: F[A], z: B)(f: (B,A) => B): B = foldLShape(fa, z)(f)._1
 5
 6   def foldMap[A,B](fa: F[A])(f: A => B)(implicit F: Monoid[B]): B = foldLShape(fa, F.zero)((b, a) => F.append(b, f(a)))._1
 7
 8   override def foldRight[A, B](fa: F[A], z: => B)(f: (A, => B) => B) =
 9     foldMap(fa)((a: A) => (Endo.endo(f(a, _: B)))) apply z
10 ...

这个foldMap就是一个游览可折叠结构的函数。在游览过程中用Monoid append对结构中元素进行操作。值得注意的是这个f: A => B参数:这个函数是用来在append操作之前先对内部元素进行一次转变(transform):

1 List(1,2,3) foldMap {x => x}                      //> res18: Int = 6
2 List(1,2,3) foldMap {x => (x + 3).toString}       //> res19: String = 456 变成String操作

我们试着用一些实际的例子来示范Monoid的用法。上面提到Monoid在可折叠数据结构里的元素连续处理有着很好的应用,我们先试一个例子:确定一个可折叠数据结构F[A]中的元素A是否排序的:

def ordered(xs: List[Int]): Boolean  //判断xs是否按序排列

由于我们必须游览List xs,所以用Monoid对元素Int进行判断操作是可行的方法。我们先设计一个对比数据结构:

Option[(min: Int, max: Int. ordered: Boolean)], 它记录了当前元素的状态,包括最小,最大,是否排序的:

 1 /判断xs是否是排序的
 2 def ordered(xs: List[Int]): Boolean = {
 3     val monoid = new Monoid[Option[(Int,Int,Boolean)]] {  //对类型Option[(Int,Int,Boolean)]定义一个Monoid实例
 4         def zero = None
 5         def append(a1: Option[(Int,Int,Boolean)], a2: => Option[(Int,Int,Boolean)]) =  //对连续两个元素进行对比操作
 6           (a1,a2) match {
 7             case (x,None) => x
 8             case (None,x) => x    //保留不为None的状态
 9             case (Some((min1,max1,ord1)),Some((min2,max2,ord2))) =>  //如果max1 <= min2状态即为true
10                  Some((min1 min min2, max1 max max2, ord1 && ord2 && (max1 <= min2)))  //更新min,max和ord
11           }
12     }  //我们需要把元素转换成Option((Int,Int,Boolean))
13     (xs.foldMap(i => Option((i, i, true)))(monoid)).map(_._3) getOrElse(true)
14 }                                                 //> ordered: (xs: List[Int])Boolean
15
16 ordered(List(1,2,12,34))                          //> res21: Boolean = true
17 ordered(List(1,2,34,23))                          //> res22: Boolean = false

注意这个i => Option((i,i,true)) 转换(transform)。

由于Monoid是种极简单的类型,所以很容易对Monoid进行组合。Monoid组合产生的结果还是Monoid,并且用起来可以更方便:

1 def productMonoid[A,B](ma: Monoid[A], mb: Monoid[B]): Monoid[(A,B)] = new Monoid[(A,B)] {
2     def zero = (ma.zero, mb.zero)
3     def append(x: (A,B), y: => (A,B)): (A,B) = (ma.append(x._1, y._1), mb.append(x._2, y._2))
4 }                                                 //> productMonoid: [A, B](ma: scalaz.Monoid[A], mb: scalaz.Monoid[B])scalaz.Mon
5                                                   //| oid[(A, B)]
6 val pm = productMonoid(Monoid[Int],Monoid[List[Int]])
7                                                   //> pm  : scalaz.Monoid[(Int, List[Int])] = Exercises.monoid$$anonfun$main$1$$a
8                                                   //| [email protected]

以上的pm就是两个Monoid的组合,结果是一个tuple2Monoid。我们可以使用这个tuple2Monoid对可折叠数据结构中元素进行并行操作。比如我们可以在游览一个List[Int]时同时统计长度(list length)及乘积(product):

 1 val intMultMonoid = new Monoid[Int] {
 2     def zero = 1
 3     def append(a1: Int, a2: => Int): Int = a1 * a2
 4 }                                                 //> intMultMonoid  : scalaz.Monoid[Int] = Exercises.monoid$$anonfun$main$1$$ano
 5                                                   //| [email protected]
 6 def productMonoid[A,B](ma: Monoid[A], mb: Monoid[B]): Monoid[(A,B)] = new Monoid[(A,B)] {
 7     def zero = (ma.zero, mb.zero)
 8     def append(x: (A,B), y: => (A,B)): (A,B) = (ma.append(x._1, y._1), mb.append(x._2, y._2))
 9 }                                                 //> productMonoid: [A, B](ma: scalaz.Monoid[A], mb: scalaz.Monoid[B])scalaz.Mon
10                                                   //| oid[(A, B)]
11 val pm = productMonoid(Monoid[Int @@ Tags.Multiplication],Monoid[Int])
12                                                   //> pm  : scalaz.Monoid[([email protected]@[Int,scalaz.Tags.Multiplication], Int)] = Exe
13                                                   //| [email protected]
14 List(1,2,3,4,6).foldMap(i => (i, 1))(productMonoid(intMultMonoid,Monoid[Int]))
15                                                   //> res23: (Int, Int) = (144,5)

我们再来一个合并多层map的Monoid:

 1 def mapMergeMonoid[K,V](V: Monoid[V]): Monoid[Map[K, V]] =
 2   new Monoid[Map[K, V]] {
 3     def zero = Map[K,V]()
 4     def append(a: Map[K, V], b: => Map[K, V]) =
 5       (a.keySet ++ b.keySet).foldLeft(zero) { (acc,k) =>
 6         acc.updated(k, V.append(a.getOrElse(k, V.zero),
 7                             b.getOrElse(k, V.zero)))
 8       }
 9   }                                               //> mapMergeMonoid: [K, V](V: scalaz.Monoid[V])scalaz.Monoid[Map[K,V]]
10
11  val M: Monoid[Map[String, Map[String, Int]]] = mapMergeMonoid(mapMergeMonoid(Monoid[Int]))
12                                                   //> M  : scalaz.Monoid[Map[String,Map[String,Int]]] = Exercises.monoid$$anonfun
13                                                   //| [email protected]
14  val m1 = Map("o1" -> Map("i1" -> 1, "i2" -> 2))  //> m1  : scala.collection.immutable.Map[String,scala.collection.immutable.Map[
15                                                   //| String,Int]] = Map(o1 -> Map(i1 -> 1, i2 -> 2))
16  val m2 = Map("o1" -> Map("i2" -> 3))             //> m2  : scala.collection.immutable.Map[String,scala.collection.immutable.Map[
17                                                   //| String,Int]] = Map(o1 -> Map(i2 -> 3))
18  val m3 = M.append(m1, m2)                        //> m3  : Map[String,Map[String,Int]] = Map(o1 -> Map(i1 -> 1, i2 -> 5))

我们可以用这个组合成的M的append操作进行map的深度合并。m1,m2合并后:Map(o1->Map("i1"->1,"i2" -> 5))。

我们还可以用这个Monoid来统计一段字串内字符发生的频率:

1 def frequencyMap[A](as: List[A]): Map[A, Int] =
2     as.foldMap((a: A) => Map(a -> 1))(mapMergeMonoid[A, Int](Monoid[Int]))
3                                                   //> frequencyMap: [A](as: List[A])Map[A,Int]
4 frequencyMap("the brown quik fox is running quikly".toList)
5   //> res24: Map[Char,Int] = Map(e -> 1, s -> 1, x -> 1, n -> 4, y -> 1, t -> 1,
6   //| u -> 3, f -> 1, i -> 4,   -> 6, q -> 2, b -> 1, g -> 1, l -> 1, h -> 1, r -
7   //| > 2, w -> 1, k -> 2, o -> 2)

我们现在可以体会到Monoid必须在可折叠数据结构(Foldable)内才能正真发挥作用。

时间: 2024-11-03 04:19:50

Scalaz(8)- typeclass:Monoid and Foldable的相关文章

Scalaz(4)- typeclass:标准类型-Equal,Order,Show,Enum

Scalaz是由一堆的typeclass组成.每一个typeclass具备自己特殊的功能.用户可以通过随意多态(ad-hoc polymorphism)把这些功能施用在自己定义的类型上.scala这个编程语言借鉴了纯函数编程语言Haskell的许多概念.typeclass这个名字就是从Haskell里引用过来的.只不过在Haskell里用的名称是type class两个分开的字.因为scala是个OOP和FP多范畴语言,为了避免与OOP里的type和class发生混扰,所以就用了typeclas

Scalaz(7)- typeclass:Applicative-idomatic function application

Applicative,正如它的名称所示,就是FP模式的函数施用(function application).我们在前面的讨论中不断提到FP模式的操作一般都在管道里进行的,因为FP的变量表达形式是这样的:F[A],即变量A是包嵌在F结构里的.Scalaz的Applicative typeclass提供了各种类型的函数施用(function application)和升格(lifting)方法.与其它scalaz typeclass使用方式一样,我们只需要实现了针对自定义类型的Applicativ

Scalaz(25)- Monad: Monad Transformer-叠加Monad效果

中间插播了几篇scalaz数据类型,现在又要回到Monad专题.因为FP的特征就是Monad式编程(Monadic programming),所以必须充分理解认识Monad.熟练掌握Monad运用.曾经看到一段对Monad的描述:“Monadic for-comprehension就是一种嵌入式编程语言,由它的Monad提供它的语法”.但如果每一种Monad的for-comprehension都独立提供一套语法的话,这种编程语言就显得十分单调.功能简单了.那么既然是FP,我们应该可以通过函数组合

Scalaz(10)- Monad:就是一种函数式编程模式-a design patter

Monad typeclass不是一种类型,而是一种程序设计模式(design pattern),是泛函编程中最重要的编程概念,因而很多行内人把FP又称为Monadic Programming.这其中透露的Monad重要性则不言而喻.Scalaz是通过Monad typeclass为数据运算的程序提供了一套规范的编程方式,如常见的for-comprehension.而不同类型的Monad实例则会支持不同的程序运算行为,如:Option Monad在运算中如果遇到None值则会中途退出:State

Scalaz(41)- Free :IO Monad-Free特定版本的FP语法

我们不断地重申FP强调代码无副作用,这样才能实现编程纯代码.像通过键盘显示器进行交流.读写文件.数据库等这些IO操作都会产生副作用.那么我们是不是为了实现纯代码而放弃IO操作呢?没有IO的程序就是一段烧CPU的代码,没有任何意义,所以任何类型的程序都必须具备IO功能,而在FP模式中对IO操作有特别的控制方式:具体实现是通过把代码中产生副作用的部分抽离出来延后运算(在所有纯代码运算之后).scalaz的IO Monad就是处理副作用代码延后运算的一种数据结构.我先举个简单的例子来示范如何通过一种数

Scalaz(12)- Monad:Writer - some kind of logger

通过前面的几篇讨论我们了解到F[T]就是FP中运算的表达形式(representation of computation).在这里F[]不仅仅是一种高阶类型,它还代表了一种运算协议(computation protocol)或者称为运算模型好点,如IO[T],Option[T].运算模型规范了运算值T的运算方式.而Monad是一种特殊的FP运算模型M[A],它是一种持续运算模式.通过flatMap作为链条把前后两个运算连接起来.多个flatMap同时作用可以形成一个程序运行链.我们可以在flat

Scalaz(43)- 总结 :FP就是实用的编程模式

完成了对Free Monad这部分内容的学习了解后,心头豁然开朗,存在心里对FP的疑虑也一扫而光.之前也抱着跟大多数人一样的主观概念,认为FP只适合学术性探讨.缺乏实际应用.运行效率低,很难发展成现实的软件开发模式.Free Monad的出现恰恰解决我心中的疑问,更正了我对FP的偏见:Free Monad提供了一套在Monad 算法内(在 for-comprehension内)的行令编程(imperative programming)方法,解决了FP的复杂语法,使Monadic编程更贴近传统编程

Scalaz(11)- Monad:你存在的意义

前面提到了scalaz是个函数式编程(FP)工具库.它提供了许多新的数据类型.拓展的标准类型及完整的一套typeclass来支持scala语言的函数式编程模式.我们知道:对于任何类型,我们只需要实现这个类型的typeclass实例就可以在对这个类型施用所对应typeclass提供的所有组件函数了(combinator).突然之间我们的焦点好像都放在了如何获取typeclass实例上了,从而忽略了考虑为什么要使用这些typeclass及使用什么样的typeclass这些问题了.所以可能有人会问我:

Scalaz(40)- Free :versioned up,再回顾

在上一篇讨论里我在设计示范例子时遇到了一些麻烦.由于Free Monad可能是一种主流的FP编程规范,所以在进入实质编程之前必须把所有东西都搞清楚.前面遇到的问题主要与scalaz Free的FreeC类型有关系.这个类型主要是针对一些非Functor的F[A]特别设计的.FreeC是Coyoneda[F,A]的Free Monad类型,任何F[A]都可以被转换成Coyoneda[F,A],而Coyoneda[F,A]本身是个Functor.因为我们通常都在Functor和非Functor AD