fmap (*2) (+100) 1
看到這樣的程式碼你能想的到結果是什麼嗎?好像跟我們平常在 map 時的操作不太一樣,感覺好像少了一個「可以被 map 的東西」。
先說結論上面的輸出結果是 202
,但為什麼?還記得我們昨天討論到 functor 的意思是只要可以被 map 就代表他是 functor,那這裡被 map 的「東西」是?
用一個比較不精確的說法是我們把 (*2)
map 到 (+100)
上後產生一個新的 function ,就像如果我們是用 fmap (*2) [1..5]
一樣我們是將 (*2)
map 到 [1..5]
進而產生新的 list。
所以我們暫時可以把 (+100)
當作一種容器
但他鐵定不是容器,這只是方便想像的解釋。
我們把 (*2)
map 到 (+100)
後應該會產生一個類似於 (*3) . (+100)
的東西。
再次提醒這只是為了方便想像的舉例。
:t fmap
-- fmap :: Functor f => (a -> b) -> f a -> f b
:t fmap (*2)
-- fmap (*2) :: (Functor f, Num b) => f b -> f b
:t fmap (*2) (+100)
-- fmap (*2) (+100) :: Num b => b -> b
會看到 fmap (*2)
接受一個 Functor f
wrap住的 Num b
,
而 fmap (*2) (+100)
變成 Num b => b -> b
先想一下 (a -> b) -> f a -> f b
及 (a -> b) -> (f a -> f b)
其實是等價,所以當我們傳入兩個 function 給 fmap
時,最後的 type
就會是 Num b => b -> b
。
這個技巧又叫做 lifting
我們目前都操作 functor 時所傳入的函數都是一個參數的,但如果我想要 map
時可以有兩個參數呢?
foo = fmap (*) [1..5]
:t foo
-- foo :: (Num a, Enum a) => [a -> a]
像是上面的程式碼,這邊會看到我們把 *
傳入 fmap
第一個參數 (a→b)
中,然後把他 map 到 list 裡
可以想像成有一個 list 可能去長這樣 [1* , 2*, 3*, 4* 5*]
這當然是為了方便想像的範例。
然後我再次 fmap
它
fmap (\f -> f 2) foo -- [2,4,6,8,10]
這邊可以想像成我們用將 [1* , 2*, 3*, 4* 5*]
取出來逐一 apply 給 2
。
那如果換成今天我想要將 Just (3 *)
map 到 Just (5)
呢?當然我們還是能在第二次 fmap 時利用 pattern matching 匹配出 Just
然後在 map 但感覺好像有點麻煩。
所以這類有關 functor 的進階操作就是交給 Applicative
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
首先看到這個typeclass 的型別約束已經說明了如果他是 Applicative
那它也必須是 Functor
然後會看到兩個 function pure
及 (<*>)
,pure
主要是傳入一個數值 a
並會回傳被 Applicative f
wrap 住的 a
,而<*>
看起來有點像是 fmap
但他是接受一個裝有 function 的 functor 及另外一個 functor 後回傳一個 functor,簡單解釋像是將第一個 functor 的 function 取出來map 到第二個 functor 最後回傳一個 functor 。
先回到上面的範例,我們簡單的用用看 <*>
及 pure
import Control.Applicative
let foo = fmap (*) [1..5]
let result = fmap (\f x -> f x) foo <*> pure 2
result -- [2,4,6,8,10]
首先我們將 (*)
map 到 [1..5]
所以這時他的 type 為 [a→a]
接下來我們再將 (\f x -> f x)
map 到 foo
依然是 [a→a]
因為 list 是 Functor
的 instance ,所以我們可以用 <*>
將 f (a→b)
變成了 f a → f b
,也就是我可以傳入另外一個 functor 來 map 這個 functor
最後因為這裡已經是 fa → fb
,所以當我傳入 pure 2
這個 f a
的時候就會回傳 f b
也就是 [2,4,6,8,10]
寫到一半發現時間不太夠了只好把剩下的部分留到明天了,
做個簡單的小結,我認為 Applicative 是一個增強版的 functor 它幫助我們可以更簡單的操作 functor,特別是兩個 functor 之間的操作。