通过Scala理解什么是Monad


什么是Monad?

trait Monad[+T] {  
    def flatMap[U]( f : (T) => Monad[U] ) : Monad[U]  
    def unit(value : B) : Monad[B]
}

Monads 就是一个values的容器,并且这个“容器”必须有一个flatMap和一个unit(v)操作. 

flatMap 将monad中的一个值转换为仍在相同monad类型中的另外一个值。

unit(v), 用来包装一个values。比如Some(v),Success(r), List(v) 

所有monads都可以直接实现flatMap, 但是每个具体的monad必须自己实现unit(v). 在scala里,一般通过构造函数或伴生对象的apply方法来实现。


至于常用的map方法,其实只是flatMap的一个特殊形式,所以map方法不是monad所必须的:

def map[U](f : (T) => U) : Monad[U] = flatMap(v => unit(f(v)))


为什么要Monad?

(1)解决结果的不确定性问题

如果我们有一连串的操作,但是这些操作的结果是不确定的(可能得到某值,也可能抛出异常或一个非法值),当我们要把多个操作连在一起调用的时候,我们必须判断每个函数的结果是否合法,只有合法的情况下才能继续调用下一个操作,但这不是我们想要的。我们总是想尽可能的推迟知道有异常发生,而像一切都正常那样连续调用一系列操作。

比如:

val result: Try[Int] = for (
    v <- Try("5".toInt);
    k <- Try("6a".toInt);
    z <- Try("9".toInt)
  ) yield( v + k + z)

其中某一步可能会异常,但是直到必须去看这个“薛定谔的猫”之前,我们并不想关心这件事。Monad模式可以帮助我们实现这个效果。


(2)解决副作用的问题

另外一个真实世界中无法用函数式完美解决的问题是IO操作,因为IO无论如何总要伴随状态的产生或变化(也就是副作用)。




Monad来历

1990 -一个由Simon Peyton-Jones、Paul Hudak、Philip Wadler、Ashton Kutcher和善待动物组织(PETA)
组成的委员会创造了Haskell,一种纯函数式的、非严求值的语言。
Haskell由于使用了Monad这种较费解的概念来控制副作用而遭到了一些批评意见。
Wadler试图平息这些质疑,他解释说:
“一个单子(Monad)说白了不过就是自函子范畴上的一个幺半群而已,这有什么难以理解的?”


什么是幺半群?


G为非空集合,如果在G上定义的二元运算 *,满足

 

(1)封闭性(Closure):对于任意a,b∈G,有a*b∈G

(2)结合律(Associativity):对于任意a,b,c∈G,有(a*b)*c=a*(b*c)

(3)幺元 (Identity):存在幺元e,使得对于任意a∈G,e*a=a*e=a

(4)逆元:对于任意a∈G,存在逆元a^-1,使得a^-1*a=a*a^-1=e

 

则称(G,*)是群,简称G是群。


半群(semigroup)

如果仅满足封闭性和结合律,则称G是一个半群(Semigroup)


幺半群(monoid)

如果仅满足封闭性、结合律并且有幺元,则称G是一个含幺半群(Monoid)。


这些数学概念和Monad实现的对应关系

trait Monad[+T] 就等于一个含幺半群G

def flatMap[U]( f : (T) => Monad[U] ) : Monad[U] 就等于是其中的二元运算 *,满足封闭性和结合律

def unit(value : B) : Monad[B] 就等于是幺元(Identity),对于任意a∈G,满足e*a=a*e=a


所以对于一个Monad,他满足半群应满足的规则:

(1)结合律/封闭性

monad.flatMap(f).flatMap(g) == monad.flatMap(v => f(v).flatMap(g)) // associativity

实例:

"be associative" in {
  val multiplier : Int => Option[Int] = v => Some(v * v)
  val divider : Int => Option[Int] = v => Some(v/2)  
  val original = Some(10)  
  original.flatMap(multiplier).flatMap(divider) === 
     original.flatMap(v => multiplier(v).flatMap(divider))
}


(2)左幺元

unit(x).flatMap(f) == f(x)

实例:

"be left unit" in {  val multiplier : Int => Option[Int] = v => Some(v * v)  val item = Some(10).flatMap(multiplier)  item === multiplier(10)}


(3)右幺元

monad.flatMap(unit) == monad

实例:

"be right unit" in {
  val value = Some(50).flatMap(v => Some(v))  
  value === Some(50)
}


什么是范畴?

范畴可简单理解为高阶类型(如List[T+]),即范畴是一组类型的集合。

假设这里有两个范畴:范畴C1 里面有类型String 和类型 Int;范畴C2 里面有 List[String] 和List[Int]

通过Scala理解什么是Monad

对于”范畴C2″,它的所有类型都是List[T]的特定类型,这个范畴就可以抽象为List高阶类型。

那对于”范畴C1″呢?它又怎么抽象?其实,”范畴C1″的抽象类型可以看做是一个Identity类型构造器,它与任何参数类型作用构造出的类型就是参数类型:scala> type Id[T] = T


什么是自函子?


1. 什么是函子(Functor)?

函数(function)表达的映射关系在类型上体现在特定类型(proper type)之间的映射

函子(functor) 则是体现在高阶类型(确切的说是范畴)之间的映射

举例来说:

// 函数, Int => String
def foo(i:Int): String = i.toString

// 函子, List[T] => Set[T] 
def baz[T](l:List[T]): Set[T] = l.toSet


2. 自函子(Endofunctor)是什么?

自函数是把一个类型映射到自身类型,比如Int=>Int, String=>String 等

自函子就是一个将范畴映射到自身的函子 (A functor that maps a category to itself)


3. flatMap就是一个自函子

def flatMap[U]( f : (T) => Monad[U] ) : Monad[U]

Monad[T+]中的flatMap,就是将Monad[T+]范畴映射到Monad[U]上的自函子!(Monad[U]其实也就是Monad[T+])



你可能感兴趣的:(通过Scala理解什么是Monad)