Scala 中的 Monads

概述

一个 monad 是一个参数化类型 M[T],支持两种操作:flatMap 和 unit,并且满足一些定律。Monad 可定义为:

trait M[T]{
    def flatMap[U](f: T => M[U]): M[U]
}

def unit[T](x: T): M[T]

Scala 中,monad 的例子有:

  • List 是一个 monad,存在 unit(x) = List(x)
  • Set 是一个 monad,存在 unit(x) = Set(x)
  • Option 是一个 monad,存在 unit(x) = Some(x)

并且每种类型都定义了 flatMap 操作。

map 不是 monad 的基本操作,这是因为 map 可以通过 flatMap 和 unit 实现:

m map f == m flatMap (x => unit(f(x)))
        == m flatMap (f andThen unit)

不过 Scala 中并没有 unit 方法,所以直接定义了 map 方法。

Monad 定律

一个 Monad 需要满足以下定律:

结合律

m flatMap f flatMap g == m flatMap (x => f(x) flatMap g)

结合律说明我们可以从左到右依次应用 flatMap;也可以叠加多个 flatMap 再应用

Left unit

unit(x) flatMap f = f(x)

Left unit 说明如果我们将 x 包装成 monad,用函数 f 做 flatMap,那么结果等价于直接对 x 应用 f。

Right unit

m flatMap unit == m

Right unit 说明如果对一个 monad flatMap 一个 unit,那么结果还是原来的 monad。

Monad 定律作用

我们知道 Scala 编译器会将 for 表达式转换成 map,flatMap 和 withFilter 等高阶函数,而Monad 定律可以帮助简化 for 表达式。

借助结合律,我们可以内联嵌套 for 表达式:

for (y <- for (x <- m 
               y <- f(x) yield y
     z <- g(y)) yield z

等价于

for (x <- m
     y <- f(x)
     z <- g(y)) yield z

Right unit 可证明以下两个表达式等价

for(x <- m) yield x
== m

不过 Left unit 在 for 表达式中还没有具体应用。


Previous post: 理解 CanBuildFrom

Next post: Scala implicit