圖解 Monad

阮一峰發表於2015-07-16

函數語言程式設計有一個重要概念,叫做Monad

網上有很多解釋(這裡這裡),但都很抽象,不容易看懂。我嘗試了好多次,還是不明白Monad到底是什麼。

昨天,我讀到了Aditya Bhargava的文章,他畫了很多圖。我想了半天,終於恍然大悟。下面,我就用這些圖來解釋Monad。

1.

軟體最基本的資料,就是各種值(value)。

2.

處理值的一系列操作,可以封裝成函式。輸入一個值,會得到另一個值。上圖的"(+3)"就是一個函式,對輸入的值加上3,再輸出。

3.

函式很像漏斗,上面進入一個值,下面出來一個值。

4.

函式可以連線起來使用,一個函式接著另一個函式。

5.

函式還可以依次處理資料集合的每個成員。

6.

說完了函式,再來看第二個概念:資料型別(type)。

資料型別就是對值的一種封裝,不僅包括值本身,還包括相關的屬性和方法。上圖就是2的封裝,從此2就不是一個單純的值,而是一種資料型別的例項,只能在資料型別的場景(context)中使用。

7.

2變成資料型別以後,原來的函式就不能用了。因為"(+3)"這個函式是處理值的(簡稱"值函式"),而不是處理資料型別的。

8.

我們需要重新定義一種運算。它接受"值函式"和資料型別的例項作為輸入引數,使用"值函式"處理後,再輸出資料型別的另一個例項。上圖的fmap就代表了這種運算。

9.

fmap的內部,實際上是這樣:開啟封裝的資料型別,取出值,用值函式處理以後,再封裝回資料型別。

10.

一個有趣的問題來了。如果我們把函式也封裝成資料型別,會怎樣?

上圖就是把函式"(+3)"封裝成一種資料型別。

11.

這時,就需要再定義一種新的運算。它不是值與值的運算,也不是值與資料型別的運算,而是資料型別與資料型別的運算。

上圖中,兩個資料型別進行運算。首先,取出它們各自的值,一個是函式,一個是數值;然後,使用函式處理數值;最後,將函式的返回結果再封裝進資料型別。

12.

函式可以返回值,當然也可以返回資料型別。

13.

我們需要的是這樣一種函式:它的輸入和輸出都是資料型別。

14.

這樣做的好處是什麼?

因為資料型別是帶有運算方法的,如果每一步返回的都是資料型別的例項,我們就可以把它們連線起來。

15.

來看一個例項,系統的I/O提供了使用者的輸入。

16.

getLine函式可以將使用者的輸入處理成一個字串型別(STR)的例項。

17.

readfile函式接受STR例項當作檔名,返回一個檔案型別的例項。

18.

putStrLn函式將檔案內容輸出。

19.

所有這些運算連起來,就叫做Monad。

簡單說,Monad就是一種設計模式,表示將一個運算過程,透過函式拆解成互相連線的多個步驟。你只要提供下一步運算所需的函式,整個運算就會自動進行下去。

(完)

相關文章