單子
單子(Monad)是一種將函子組合應(yīng)用的方法斗幼。在計(jì)算機(jī)科學(xué)里,單子經(jīng)常用來代表計(jì)算(computation)孟岛。單子能用來把與業(yè)務(wù)無關(guān)的通用程序行為抽象出來,比如有用來處理并行(Future)渠羞、異常(Option和Try等)、甚至副作用的單子次询。
單子的flatMap和unit操作作為構(gòu)建數(shù)據(jù)類型的基本操作,可以實(shí)現(xiàn)很多復(fù)雜的高階函數(shù)屯吊。
單子的程序描述
Monad定義了unit和flatMap兩個(gè)函數(shù)。Monad都是Functor盒卸,因?yàn)槲覀兛梢杂胒latMap+unit來實(shí)現(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)建一個(gè)Monad實(shí)例虹蓄。unit表示“裝箱”。
- flatMap和map函數(shù)類似薇组,不同的是它接受的參數(shù)f返回的是M[B]。flatMap會(huì)對(duì)Monad里的每一個(gè)元素都產(chǎn)生一個(gè)新的Monad容器律胀,然后所有這些容器里的元素會(huì)被取出,而組合到一個(gè)Monad容器里炭菌。
- unit,map,flatMap是Monad的三個(gè)必要函數(shù),其中map可以由unit和flatMap來實(shí)現(xiàn)
def map[A, B](f: A => B): M[B] =
flatMap(a => unit(f(a)))
Monad類代碼
上面是定義了Monad特質(zhì)娃兽,定義了Monad需要實(shí)現(xiàn)的unit和flatMap接口,而實(shí)際的monad是指M[_]
這個(gè)類型構(gòu)造器。
而下面的M則是指一個(gè)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阔馋,它接受一個(gè)包裹兩層的類型,轉(zhuǎn)換成包裹一層的類型娇掏。
其實(shí)flatten可以通過flatMap推導(dǎo)出來,def flatten[A](mma: M[M[A]]): M[A] = flatMap(mma)(ma => ma)
婴梧,所以這個(gè)定義和之前的Monad特質(zhì)的定義時(shí)等價(jià)的。
單子法則
結(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)
塞蹭,這對(duì)于Monad來說,用flatMap難以表達(dá)該操作番电。
如果不對(duì)Monadic值M[A]進(jìn)行結(jié)合性操作,而是對(duì)Monadic函數(shù)A => M[B]證明結(jié)合性操作就會(huì)相對(duì)容易漱办。
A => M[B]
是瑞士數(shù)學(xué)家Heinrich Kleisli法則的箭頭(Kleisli Arrow)。我們可以用Kleisli Arrow來實(shí)現(xiàn)一個(gè)函數(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相對(duì)于op操作的作用洞辣,在Monad中,unit操作是compose函數(shù)的元函數(shù)屋彪。
通過unit我們可以證明Monad的左右恒等:
compose(f,unit) == f
compose(unit,f) == f
unit操作還滿足下面兩個(gè)等式:
unit(x) flatMap f == f(x)
m flatMap unit == m
單子的形象解釋
如果說functor是應(yīng)用一個(gè)函數(shù)到包裹的值所宰,那么monad則是應(yīng)用一個(gè)返回包裹值的函數(shù)到一個(gè)包裹的值畜挥。
上圖表示婴谱,首先獲得一個(gè)Monad,其次定義一個(gè)返回Monad的函數(shù)如half谭羔,最后結(jié)果也會(huì)返回一個(gè)Monad。
這里half函數(shù)是輸入一個(gè)值然后返回一個(gè)包裹的值瘟裸,如果輸入的是一個(gè)包裹的值,那么代碼就不工作了。如下面兩幅圖所示:
Monad在輸入一個(gè)包裹值到一個(gè)函數(shù)的過程中要做到的是:
- 綁定已經(jīng)解除包裹的值
- 將已經(jīng)解除包裹的值輸入函數(shù)
- 一個(gè)被重新包裹的值被輸出
那么卵慰,對(duì)一個(gè)包裹的值應(yīng)用多次half函數(shù)將是這樣的:
小結(jié)
Monoid是元素對(duì)象的組合的范疇,如果這種元素對(duì)象是函數(shù)或函子裳朋,那么Monad是自函子的組合范疇,Monad也是一種特殊的Monoid子集鲤嫡。
所以正應(yīng)了那句名言“單子說白了不過就是自函子范疇上的一個(gè)幺半群而已(A monad is just a monoid in the category of endofunctors)”。
轉(zhuǎn)載請(qǐng)注明作者Jason Ding及其出處
jasonding.top
Github博客主頁(http://blog.jasonding.top/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡(jiǎn)書主頁(http://www.reibang.com/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354進(jìn)入我的博客主頁