單子
單子(Monad)是一種將函子組合應(yīng)用的方法。在計算機科學(xué)里,單子經(jīng)常用來代表計算(computation)。單子能用來把與業(yè)務(wù)無關(guān)的通用程序行為抽象出來,比如有用來處理并行(Future)、異常(Option和Try等)、甚至副作用的單子。
單子的flatMap和unit操作作為構(gòu)建數(shù)據(jù)類型的基本操作,可以實現(xiàn)很多復(fù)雜的高階函數(shù)。
單子的程序描述
Monad定義了unit和flatMap兩個函數(shù)。Monad都是Functor,因為我們可以用flatMap+unit來實現(xiàn)map。我們可以定義Monad繼承自Functor特質(zhì)。
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
trait Monad[M[_]] extends Functor[M] {
def unit[A](a: A): M[A]
def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B]
}
- unit可以看成是Monad的構(gòu)造函數(shù)或者工廠函數(shù),它用來創(chuàng)建一個Monad實例。unit表示“裝箱”。
- flatMap和map函數(shù)類似,不同的是它接受的參數(shù)f返回的是M[B]。flatMap會對Monad里的每一個元素都產(chǎn)生一個新的Monad容器,然后所有這些容器里的元素會被取出,而組合到一個Monad容器里。
- unit,map,flatMap是Monad的三個必要函數(shù),其中map可以由unit和flatMap來實現(xiàn)
def map[A, B](f: A => B): M[B] =
flatMap(a => unit(f(a)))
Monad類代碼
上面是定義了Monad特質(zhì),定義了Monad需要實現(xiàn)的unit和flatMap接口,而實際的monad是指M[_]
這個類型構(gòu)造器。
而下面的M則是指一個Monad類:
class M[A] {
def flatMap[B](f: A => M[B]): M[B] = ...
}
def unit[A](x: A): M[A] = ...
其他風(fēng)格的Monad代碼
有的Monad風(fēng)格是這樣的:
trait Monad[M[_]] {
def unit[A](a: A): M[A]
def flatten[A](mma: M[M[A]]): M[A]
}
上面的flatten也叫join或者bind,它接受一個包裹兩層的類型,轉(zhuǎn)換成包裹一層的類型。
其實flatten可以通過flatMap推導(dǎo)出來,def flatten[A](mma: M[M[A]]): M[A] = flatMap(mma)(ma => ma)
,所以這個定義和之前的Monad特質(zhì)的定義時等價的。
單子法則
結(jié)合性法則(associative law)
flatMap滿足結(jié)合性法則
m flatMap f flatMap g == m flatMap (x => f(x) flatMap g)
Kleisli組合法則(kleisli composition)
Monoid的結(jié)合性操作op(a, op(b, c)) == op(op(a, b), c)
,這對于Monad來說,用flatMap難以表達該操作。
如果不對Monadic值M[A]進行結(jié)合性操作,而是對Monadic函數(shù)A => M[B]證明結(jié)合性操作就會相對容易。
A => M[B]
是瑞士數(shù)學(xué)家Heinrich Kleisli法則的箭頭(Kleisli Arrow)。我們可以用Kleisli Arrow來實現(xiàn)一個函數(shù)compose:
def compose[A, B, C](f: A => M[B], g: B => M[C]): A => M[C] =
a => flatMap(f(a))(g)
compose函數(shù)滿足compose(f,compose(g,h)) == compose(compose(f,g),h)
。
恒等法則(identity law)
在Monoid中,identity相對于op操作的作用,在Monad中,unit操作是compose函數(shù)的元函數(shù)。
通過unit我們可以證明Monad的左右恒等:
compose(f,unit) == f
compose(unit,f) == f
unit操作還滿足下面兩個等式:
unit(x) flatMap f == f(x)
m flatMap unit == m
單子的形象解釋
如果說functor是應(yīng)用一個函數(shù)到包裹的值,那么monad則是應(yīng)用一個返回包裹值的函數(shù)到一個包裹的值。
上圖表示,首先獲得一個Monad,其次定義一個返回Monad的函數(shù)如half,最后結(jié)果也會返回一個Monad。
這里half函數(shù)是輸入一個值然后返回一個包裹的值,如果輸入的是一個包裹的值,那么代碼就不工作了。如下面兩幅圖所示:
Monad在輸入一個包裹值到一個函數(shù)的過程中要做到的是:
- 綁定已經(jīng)解除包裹的值
- 將已經(jīng)解除包裹的值輸入函數(shù)
- 一個被重新包裹的值被輸出
那么,對一個包裹的值應(yīng)用多次half函數(shù)將是這樣的:
小結(jié)
Monoid是元素對象的組合的范疇,如果這種元素對象是函數(shù)或函子,那么Monad是自函子的組合范疇,Monad也是一種特殊的Monoid子集。
所以正應(yīng)了那句名言“單子說白了不過就是自函子范疇上的一個幺半群而已(A monad is just a monoid in the category of endofunctors)”。
轉(zhuǎn)載請注明作者Jason Ding及其出處
jasonding.top
Github博客主頁(http://blog.jasonding.top/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡書主頁(http://www.lxweimin.com/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354進入我的博客主頁