前言
Scala一种结合面向对象(OO)和函数式编程(FP)的语言,学习函数式编程的话经常会遇到Monad(单子)这个概念,Monad是用于控制副作用的,这篇文章不介绍它是如何控制副作用,只介绍Monad涉及的概念。Haskell大神Philip Wadler说:
“一个单子(Monad)说白了不过就是自函子(Endofunctor)范畴上的一个幺半群(Monoid)而已,这有什么难以理解的?”
概念
范畴
范畴其实就是对特定类型的抽象,即高阶类型(也就是类型构造器)。
范畴C2就可以抽象为List高阶类型
范畴C1就可以抽象为Identity高阶类型,即
type Id[T] = T
函子(functor)
函子是体现在高阶类型(确切的说是范畴,可把范畴简单的看成高阶类型)之间的映射,如图
代码描述如下
trait Functor[F[_]] {
def map[A,B](fa: F[A], f: A=>B): F[B]
}
具体例子
//函子
trait Functor[F[_]] {
def map[A,B](fa: F[A], f: A=>B): F[B]
}
//范畴(高阶函数/类型构造器)
case class My[T](e:T)
def testMap[A,B, M <: My[A]](m:M, f: A=>B)(implicit functor:Functor[My]) = {
functor.map(m,f)
}
//My[Int], 应用函数 Int=>String 得到 My[String]
implicit object MyFunctor extends Functor[My] {
def map[A,B](fa: My[A], f:A=>B) = My(f(fa.e))
}
//输出My(200ok)
println(testMap(My(200), (x:Int)=>x+"ok"))
自函子(Endofunctor)
自函子是一类比较特殊的函子,它是一种将范畴映射到自身的函子
假设这个自函子为F,则对于 F[Int] 作用的结果仍是Int,对于函数f: Int=>String 映射的结果 F[f] 也仍是函数f
对于范畴Hask来说,如果有一个函子F,对里面的元素映射后,其结果仍属于Hask,比如我们用List这个函子
//List[A]的映射是List[List[A]...
List[A], List[List[A]], List[List[List[A]]]...
总得来说自函子就是整体结构和局部结构一致的函子
幺半群(Monoid)
群(group): G为非空集合,如果在G上定义的二元运算 *,满足
- 封闭性(Closure):对于任意a,b∈G,有a*b∈G
- 结合律(Associativity):对于任意a,b,c∈G,有(a*b)c=a(b*c)
- 幺元 (Identity):存在幺元e,使得对于任意a∈G,e*a=a*e=a
- 逆元:对于任意a∈G,存在逆元a^-1,使得a^-1*a=a*a^-1=e
则称(G,*)是群,简称G是群。
如果仅满足封闭性和结合律,则称G是一个半群(Semigroup);如果仅满足封闭性、结合律并且有幺元,则称G是一个含幺半群(Monoid)。
代码描述如下
//半群
trait SemiGroup[T] {
def append(a: T, b: T): T
}
//整数加法半群,满足封闭性和结合律
object IntSemiGroup extends SemiGroup[Int] {
def append(a: Int, b: Int) = a + b
}
现在在半群的基础上,再增加一个幺元
//幺半群
trait Monoid[T] extends SemiGroup[T] {
// 幺元
def zero: T
}
//整数加法幺半群,满足封闭性和结合律,存在幺元
object IntMonoid extends Monoid[Int] {
def append(a: Int, b: Int) = a + b
def zero = 0
}
幺半群在fold操作中体现它的好处,fold(对折),即两两进行操作,最终合成一个。因为满足结合律所以前面的操作和后面的操作可以同时进行,大大提升效率。
def acc[T](list: List[T])(implicit m: Monoid[T]) = {
//可并行执行foldleft
list.foldLeft(m.zero)(m.append)
}
单子(Monad)
单子(Monad)是自函子(Endofunctor)范畴上的一个幺半群(Monoid)
代码解释如下
trait Monad[M[_]] {
def unit[A](a: A): M[A]//装箱,相当于幺元
def flatMap[A, B](fa: M[A])(f: A => M[B]): M[B]//满足自函子和半群特征
}
所以对于一个Monad,他满足半群应满足的规则:
(1)结合律/封闭性
monad.flatMap(f).flatMap(g) == monad.flatMap(v => f(v).flatMap(g))
实例:
val multiplier : Int => Option[Int] = v => Some(v * v)
val divider : Int => Option[Int] = v => Some(v/2)
val original = Some(10)
println(original.flatMap(multiplier).flatMap(divider) == original.flatMap(v => multiplier(v).flatMap(divider)))//true
(2)左幺元
unit(x).flatMap(f) == f(x)
实例:
val item = Some(10).flatMap(multiplier)
println(item == multiplier(10))//true
(2)右幺元
monad.flatMap(unit) == monad
实例:
//右幺元
val t=multiplier(10).flatMap(v⇒Some(v))
println(t == multiplier(10))//true