試著講講看甚麼是 Monad

1.

FP 想要 purely function,不喜歡 side effect。但在現實中就是會有 side effect 的存在。FP 建立了一種會員制的 Club,任何有遇到 side effect 的計算,沒關係。我們讓當中 pure 的成分,提升到 Club。而其他有 side effect 的成份,先讓圍事讓他們在 Club 外面排隊等。至少我們在 Club 內部,可以宣稱全部是 pure 的。

這種會產生 side effect 而要建立 Club 機制的計算,我們先把它標記成 C++ 語言的 function template 形式
 

template<typename InputT, typename ReturnT>
Club<ReturnT> func1(InputT arg);

 
我們把登記會員這個動作叫做 lift,提升資格的意思。另一種函式形式的叫法是叫 pure(x),代表把普通人 x 標記 成 Club 內的 pure x

2.

當你想繼續算下去時,發現其他寫好的計算過程,其實不知道所謂的 Club 機制,而是很單純處理一般資料。像是下面:

template<typename InputT, typename ReturnT>
ReturnT func2(InputT arg);

那麼你從 func1 算出來的 Club<ReturnT>func2 是吃 InputT 而不是 Club<InputT> 的,那要怎麼套用 func2 呢?這個時候有些 Club 比較厲害,有 警衛室 可以 代收這種計算過程,幫你跟裡面的人講,大家把自己代一代,再把結果代為傳出來。

template<typename InputT, typename ReturnT>
Club<ReturnT> GuardOffice(Club<InputT> clubedData, std::function<ReturnT(InputT)> func2);

有這樣設立警衛室的 Club 很棒,我們會叫這種代收運算過程的 Club 為 Functor。而警衛室的功能呢,用一個比較廣義的詞,叫 fmap ,代表我幫你 計算過程,來去 map (把自己代一代) Club 裡面的會員。 

3.

有了警衛室的功能後,你會想, 那這樣我不就可以請警衛幫我代收是一堆計算過程,像是 

template<typename InputT, typename ReturnT> 
Club<ReturnT> func3(InputT arg);
template<typename InputT, typename ReturnT> 
Club<ReturnT> func4(InputT arg);

InputData arg;
Club<Club<Club<InputDataT>>> result = GuardOffice(GuardOffice(GuardOffice(func1(arg), func2), func3), func4)

沒錯!

但是你會發現這些 func3func4,一定要是無 Club 機制的計算過程。否則因為 警衛室 只是單純幫你代為套用,使得上面的結果會變成: 

decltype(result) ==> Club<Club<Club<InputDataT>>>;

Club 中又有 Club,之中又再有 Club。

當初建立會員制的目的,只是想要簡單一層的 Club 來幫我們隔離 side effect。並不需要這麼多層結構來複雜化,我們並不 care。於是在競爭激烈的社會下,更棒的 Club 推出 高等警衛室

不管是在第一層 Club 外面排隊的 side effect ,還是在第二層 Club 外面排隊的 side effect。我們通通都把他趕到最外面那層。我們只想要 Club 內部是純 pure,其他的我們不管。

使得

template<typename InputT, typename ReturnT> 
Club<ReturnT> func3(InputT arg);

template<typename InputT, typename ReturnT> 
Club<ReturnT> func4(InputT arg);

using SideEffectFuncT = decltype(func3);

template<typename InputT, typename ReturnT>
Club<ReturnT> PrimeGuardOffice(Club<InputT>, SideEffectFuncT func);`

升等之後,statement 改成

InputData arg;
Club<ReturnT> result = PrimeGuardOffice(PrimeGuardOffice(GuardOffice (func1(arg), func2), func3), func4)

可以給你好棒棒的 Club<ReturnT> 結果。 

這種推出高等警衛室功能 Club ,我們稱他為 Monad高等警衛室 的功能我會會叫 flatMap,意思表示,在將 function 套用 (/map)在內部會員後,並把他產生出來的 side effect 也趕到外面,跟外面在排隊的 side effect 一起排成只有一個隊伍 (/flat)。
 

4.

有了 Monad Club ,不管是一般的 func2 ,或是有 side effect 的 func3func4,都能以 purely function 的方式,(Composite)串聯起來。那些如何隔離 side effect 的機制,都交由 高等警衛室一般警衛室 來去來處理。

有時候會擔心我們用的 Club,經由 警衛室 轉換過後 Club 內做運算,與在 Club 外做結果,是不是等價不變的

我們會定下 對應關係要符合我們預期 的規則。剛好跟群論裡的談到 Monad 性質一樣,所以才會以他的名字命名。

comments powered by Disqus