Functor、Applicative、Monad —— Haskell趣學(xué)指南讀后感

最近在閱讀了《Haskell趣學(xué)指南》后對(duì)函數(shù)式編程有了更加深入的認(rèn)識(shí)。其中 functor 、applicative 和 monad 這一系列的概念是Haskell中重要的組成部分。寫(xiě)一下讀后感算是復(fù)習(xí)一下書(shū)中的內(nèi)容。

Functor(函子)


在 Haskell 中 functor 代表可以映射的事物。可以映射的事物比如說(shuō)是[Int](整型數(shù)組),swift中的optional類(lèi)型,字典,Set 等。概括起來(lái)凡是擁有容器性質(zhì)的類(lèi)型都可以視作 functor(函子)。在 Haskell 中 functor 是一個(gè)類(lèi)型類(lèi)(相當(dāng)于 oc 中的協(xié)議,Java 中的接口)。在 functor 類(lèi)型類(lèi)中只定義了一個(gè)方法。

fmap :: (a -> b) -> f a -> f b 

解釋一下 fmap 函數(shù)。 fmap 函數(shù)接受兩個(gè)參數(shù) (a -> b) 和 f a 。其中

(a -> b) 參數(shù)是一個(gè)函數(shù)。這個(gè)函數(shù)接受 a 類(lèi)型,返回 b 類(lèi)型

f a 參數(shù)是一個(gè)容器類(lèi)型,然后容器里面裝的類(lèi)型是 a。

f b 是 fmap 函數(shù)的返回值,是一個(gè)容器類(lèi)型,然后容器類(lèi)型里面裝的是 b。

舉一個(gè)把具體的類(lèi)型應(yīng)用到 fmap 函數(shù)的定義里面的例子:

fmap :: (Int -> String) -> [Int] -> [String]

對(duì)于這個(gè)例子,fmap 函數(shù)的作用就是接受一個(gè)把 Int 映射到 String 的函數(shù),然后把這個(gè)函數(shù)喂給 Int 數(shù)組([Int]),吧這個(gè) Int 數(shù)組變成 String 數(shù)組([String])。

image.png

做個(gè)比喻,fmap 函數(shù)做的事情就是相當(dāng)于,吧一個(gè)裝著 a 類(lèi)型的盒子,打開(kāi)然后把里面的 a 類(lèi)型拿出來(lái)變成 b 類(lèi)型,之后在返回原來(lái)的盒子。

Applicative


Applicative 是一種加強(qiáng)的 functor 。

Applicative 為什么存在?

首先我們可以把函子值是看做有上下文的值。

image.png

用盒子模型來(lái)解釋的話(huà),盒子里面的 a 就是函子值,而裝著 a 的盒子就是 a 的上下文。所以 fmap 函數(shù)是在保持上下文不變的條件下,把一個(gè)函數(shù)應(yīng)用到值上面。

上下文(也就是盒子)是什么東西都可以裝的,也就是說(shuō)上下文(盒子)里面也是可以裝函數(shù)的。比如

image.png

那么我們?cè)趺崔k吧這個(gè)盒子里的函數(shù)應(yīng)用到包裝在其他盒子里的值呢?這里就是 applicative 應(yīng)用的地方了。

Applicative 定義

在 haskell 中想要是 Applicative 類(lèi)型類(lèi)的實(shí)例的前提是已經(jīng)是 functor 類(lèi)型類(lèi)的實(shí)例。

在 Applicative 的類(lèi)型類(lèi)的定義中有兩個(gè)方法:

pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
  • pure 函數(shù):接受一個(gè)類(lèi)型 a 返回一個(gè)把這個(gè) a 放入上下文(盒子)中的類(lèi)型。意思就是把 a 打包成具有上下文的類(lèi)型。用盒子模型來(lái)理解就是把 a 裝入一個(gè)盒子里面。

  • <*> 函數(shù):接受一個(gè)具有上下文的函數(shù)(a -> b)和一個(gè)具有上下文的 a (就是 f a)為參數(shù),然后返回具有上下文的 b (就是 f b)的值。

用盒子模型來(lái)理解 <*> 函數(shù)的意思就是:

image.png

分別把函數(shù)(a -> b)和參數(shù)( a )從盒子中取出來(lái),然后把函數(shù)應(yīng)用到參數(shù)上,吧得到的值再裝回盒子里面。

Monad


Monad 是升級(jí)的 applicative。在 applicative 的基礎(chǔ)上支持綁定。但是在 Haskell 中 Monad 和 applicatvie 在代碼上并沒(méi)有聯(lián)系,因?yàn)楦鶕?jù)在 Haskell 中是先出現(xiàn) Monad 然后才出現(xiàn) applicative 的。

首先來(lái)看 Monad 的定義

return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
fail :: String -> m a

在 monad 類(lèi)型類(lèi)里面總共定義了4個(gè)函數(shù):

  • return 函數(shù):就是把 a 放入一個(gè)上下文中。和 applicative 里面的 pure 函數(shù)的作用一樣

  • >> 函數(shù):這個(gè)函數(shù)特別簡(jiǎn)單,就是無(wú)條件的把第二個(gè)參數(shù)作為返回值返回

  • fail 函數(shù):這個(gè)函數(shù)是由 Haskell 來(lái)調(diào)用,我們永遠(yuǎn)都不會(huì)顯示的調(diào)用

  • >>= 函數(shù):這個(gè)函數(shù)就是上面所說(shuō)的綁定。他接受一個(gè)帶有上下文的值和一個(gè)返回具有上下文的值的函數(shù)為參數(shù),返回最后參數(shù)的函數(shù)中的返回值。

用盒子模型來(lái)解釋綁定

image.png

綁定的過(guò)程就是首先把 a 從盒子中取出來(lái),然后把 a 作用到函數(shù),改函數(shù)直接返回了一個(gè)裝有 b 的盒子。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容