天天看點

Scalaz(14)- Monad:函數組合-Kleisli to Reader

  monad reader就是一種函數的組合。在scalaz裡函數(function)本身就是monad,自然也就是functor和applicative。我們可以用monadic方法進行函數組合:

 以上的函數f,g必須滿足一定的條件才能實作組合。這個從f(g(2))或g(f(2))可以看出:必需固定有一個輸入參數及輸入參數類型和函數結果類型必需一緻,因為一個函數的輸出成為另一個函數的輸入。在fp裡這樣的函數組合就是monadic reader。 

但是fp裡函數運算結果一般都是m[r]這樣格式的,是以我們需要對f:a => m[b],g:b => m[c]這樣的函數進行組合。這就是scalaz裡的kleisli了。kleisli就是函數a=>m[b]的類封套,從kleisli的類定義可以看出:scalaz/kleisli.scala

kleisli的目的是把monadic函數組合起來或者更形象說連接配接起來。kleisli提供的操作方法如>=>可以這樣了解:

(a=>m[b]) >=> (b=>m[c]) >=> (c=>m[d]) 最終運算結果m[d]

可以看出kleisli函數組合有着固定的模式:

1、函數必需是 a => m[b]這種模式;隻有一個輸入,結果是一個monad m[_]

2、上一個函數輸出m[b],他的運算值b就是下一個函數的輸入。這就要求下一個函數的輸入參數類型必需是b

3、m必須是個monad;這個可以從kleisli的操作函數實作中看出:scalaz/kleisli.scala

拿操作函數>=>(andthen)舉例:implicit b: bind[m]明确了m必須是個monad。

kleisli((a: a) => b.bind(this(a))(k.run))的意思是先運算m[a],接着再運算k,以m[a]運算結果值a作為下一個函數k.run的輸入參數。整個實作過程并不複雜。

實際上reader就是kleisli的一個特殊案例:在這裡kleisli的m[]變成了id[],因為id[a]=a >>> a=>id[b] = a=>b,就是我們上面提到的reader,我們看看reader在scalaz裡是如何定義的:scalar/package.scala

type readert[f[_], e, a] = kleisli[f, e, a] >>> type reader[e,a] = readert[id,e,a]

好了,說了半天還是回到如何使用kleisli進行函數組合的吧:

例子雖然很簡單,但它說明了很多重點:上一個函數輸入的運算值是下一個函數的輸入值 int=>string=>boolean。輸出monad一緻統一,都是option。

那麼,kleisli到底用來幹什麼呢?它恰恰顯示了fp函數組合的真正意義:把功能盡量細分化,通過各種方式的函數組合實作靈活的函數重複利用。也就是在fp領域裡,我們用kleisli來組合fp函數。

下面我們就用scalaz自帶的例子scalaz.example裡的kleisliusage.scala來說明一下kleisli的具體使用方法吧:

下面是一組地理資訊結構:

分别是:洲(continent)、國家(country)、城市(city)。它們之間的關系是層級的:continent(country(city))

下面是一組模拟資料:

從上面的模拟資料也可以看出continent,country,city之間的隸屬關系。我們下面設計三個函數分别對continent,country,city進行查找:

從continents,countries,cities這三個函數運算結果可以看出它們都可以獨立運算。這三個函數的款式如下:

string => list[continent]

continent => list[country]

country => list[city]

無論函數款式或者類封套(list本來就是monad)都适合kleisli。我們可以用kleisli把這三個局部函數用各種方法組合起來實作更廣泛功能:

還有個=<<符号挺有意思:

意思是用包嵌的函數flatmap一下輸入參數m[a]:

那麼如果我想避免使用list(),用option[list]作為函數輸出可以嗎?option是個monad,第一步可以通過。下一步是把函數款式對齊了:

list[string] => option[list[continent]]

list[continent] => option[list[country]]

list[country] => option[list[city]]

下面是這三個函數的更新版:

我們看到,隻要monad一緻,函數輸入輸出類型比對,就能用kleisli來實作函數組合。