函數語言程式設計有一個重要概念,叫做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就是一種設計模式,表示將一個運算過程,透過函式拆解成互相連線的多個步驟。你只要提供下一步運算所需的函式,整個運算就會自動進行下去。
(完)