更新於 2024/11/23閱讀時間約 3 分鐘

大部分的Monad教程都錯了

學習 Functional Programming 時的第一個難關就是 Monad

一般的教程都是把回傳 Monad 的函式解釋成「帶有 side effect 的函式」

然而我認為這是不合適的詮釋

也不會幫助初學者理解 (至少對我來說就是如此)

例如 List Monad 常被解釋成有多種可能的結果的 Monad

所以它的 side effect 是「多種可能的結果」?

這樣描述只會讓初學者更不懂


在更進一步討論之前

我們得先釐清何謂 side effect

這個詞主要是描述相對於 pure function 的概念

pure function 不允許在運算時對環境造成影響或被環境影響

傳入相同的參數就應該得到相同的結果

因此改變運算順序不會影響輸出結果

反之帶有 side effect 的函式指的就是會對環境造成影響的函式

例如 print(message), setValue(value), toggle(checkbox) 這類的函式

或者更應該稱呼其為「程序」

因為他們做的不只是做某種計算, 還會執行某種操作

不如說執行某種操作才是這些函式的主要目的

因此改變順序可能會造成未預期的結果

pure function 和 side effect 描述的就是這種差異


所以用這種 side effect 的概念對 Monad 的詮釋是否合適?

IO Monad 可以理解成會「輸入輸出到外部終端」的函式

它可以藉由跟外界互動做出不同的行為

因此非常適合用 side effect 詮釋

State Monad 可以理解成會「改變狀態」的函式

根據狀態會有不同的結果

因此也非常適合用 side effect 詮釋

Maybe Monad 則可以理解成「可能會失敗」的函式

這部份比較奇怪

畢竟函式失敗了就不會有回傳值了

但「可能會失敗」好像也可以說是一種 effect:

它似乎是一種可以拋出例外的程序

List Monad 就更迷了

若是把它理解成「有多種可能結果」的函式

它是一種執行什麼操作的程序?

什麼樣的操作會產生多種結果? 量子疊加? 多重宇宙?


上面對 side effect 的定義有一個重要的特性: linearity

帶有 side effect 的函式不應改變輸出結果的型別和乘數

它會輸出什麼樣的東西不應受到 side effect 的影響

我們一般不會預期一個「帶有某些效果的函式」的輸出結果與其宣告不同

因此 Maybe Monad, List Monad 不應用 side effect 詮釋

他們不是造成某些效果, 而是直接影響 control flow 的結構

我們會認為 Maybe Monad 用 side effect 詮釋說的通

是因為我們已經習慣 try catch 例外處理的 pattern

try catch 本身就是非常奇怪的語言特性, 不應把它當作理所當然


帶有 side effect 的函式的確是一種 Monad

然而用 side effect 的概念囊括所有的 Monad 就有點牽強

事實上 Monad function 只是一個數學結構

描述的是所有行為像 function 的東西

硬要給所有這類東西安上一個統一的直觀概念就很奇怪

與其用統一的方式去理解所有 Monad

不如單獨理解每個 Monad 的語意

或是好好地從數學結構去理解 Monad

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.