如果我们的有一个 functor 里面是 Just (3 *)
还有另一个 functor 里面是 Just 5
,但我们想要把第一个 Just (3 *)
map over Just 5
呢?如果是普通的 functor,那就没救了。因为他们只允许 map 一个普通的函数。即使我们用 \f -> f 9
来 map 一个装了很多函数的 functor,我们也是使用了普通的函数。我们是无法单纯用 fmap
来把包在一个 functor 的函数 map 另一个包在 functor 中的值。我们能用模式匹配Just
来把函数从里面抽出来,然后再 map Just 5
,但我们是希望有一个一般化的作法,对任何 functor 都有效。
看看 Applicative
这个 typeclass。他位在 Control.Applicative
中,在其中定义了两个函数pure
跟 <*>
。他并没有提供缺省的实作,如果我们想使用他必须要为他们 applicative functor 的实作。typeclass 定义如下:
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
- Applicative 是一个 Functor, 所以他也有fmap
pure
应该要接受一个值,然后回传一个包含那个值的 applicative functor。(对于pure
比较好的说法是把一个普通值放到一个缺省的 context 下,一个最小的 context 但仍然包含这个值。)<*>
则是接受一个装有函数的 functor 跟另一个 functor,然后取出第一个 functor 中的函数将他对第二个 functor 中的值做 map。(左结合)
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
ghci> pure (+) <*> Just 3 <*> Just 5
Just 8
pure f <*> x
等于
fmap f x
Control.Applicative
会 export 一个函数
<$>
,他基本上就是中缀版的
fmap
。他是这么被定义的:
(<$>) :: (Functor f) => (a -> b) -> f a -> f b说明:要记住类型变量跟参数的名字还有值绑定的名称不冲突。
f <$> x = fmap f x
f
在函数的类型宣告中是类型变量,说明
f
应该要满足
Functor
typeclass 的条件。而在函数本体中的
f
则表示一个函数,我们将他 map over x。我们同样用
f
来表示他们并代表他们是相同的东西。
可以将一个普通的函数套用在 applicative functor 上真不错。只要稍微写一些 <$>
跟 <*>
就可以把函数变成 applicative style,可以操作 applicatives 并回传 applicatives。
instance Applicative [] where
pure x = [x]
fs <*> xs = [f x | f <- fs, x <- xs]
instance Applicative ((->) r) where当我们用
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
pure
将一个值包成 applicative functor 的时候,他产生的结果永远都会是那个值。也就是最小的 context。那也是为什么对于 function 的
pure
实作来讲,他就是接受一个值,然后造一个函数永远回传那个值,不管他被喂了什么参数。如果你限定
pure
的类型至
(->) r
上,他就会是
pure :: a -> (r -> a)
。
ghci> :t (+) <$> (+3) <*> (*100)
(+) <$> (+3) <*> (*100) :: (Num a) => a -> a
ghci> (+) <$> (+3) <*> (*100) $ 5
508
ghci> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5当我们做
[8.0,10.0,2.5]
(+) <$> Just 3 <*> Just 5
,我们是用
+
套用在一些可能有或可能没有的值上,所以结果也会是可能有或没有。当我们做
(+) <$> (+10) <*> (+5)
,我们是将
+
套用在
(+10)
跟
(+5)
的结果上,而结果也会是一个函数,当被喂给一个参数的时候会产生结果。
instance Applicative ZipList where
pure x = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)
111