Learn You a Haskell for Great Good 读书笔记 11

11 函子以及Applicative

函子

函子是那些可以被map的类型,比如List, Maybe, Tree等。它们共有一个函数

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

IO 函子

1
2
3
4
instance Functor IO where
fmap f action = do
result <- action
return (f result)

看看上面fmap的定义是不是很清晰,IO可以最为函子来看。

(->) 也是函子

a -> b 就相当于 (->) a b, -> 是一个中缀类型构造器(卧槽这都行)。

然后我们推算一下它的fmap:

1
2
3
4
5
6
fmap :: (a -> b) -> f a -> f b
f = (->) r
fmap :: (a -> b) -> ((->) r a) -> ((->) r b)
fmap :: (a -> b) -> (r -> a) -> (r -> b)
instance Functor ((->) r) where
fmap = (.)

所以fmap 对 函数而言就是函数编组(Function Composition)

函数提升(lifting)

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

就是一个函数的入参和出参都被函子包了一层,也是fmap的另一种理解。

函子俩定律

  1. 1
    fmap id theFactor = theFactor
  2. 1
    fmap (f . g) = fmap f . fmap g

Applicative

我们想把函子包的函数(如Just (*3))应用到函子包的值里,要咋办? Applicative就干这个

1
2
3
class (Functor f) => Applicative f where 
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b

pure 把任一一个值用包成函子

<*>把函子包着的函数应用出去

为了方便,fmap 还有个中缀的版本fmap = (<$>),然后就可以骚了:

1
2
ghci> (++) <$> Just "johntra" <*> Just "volta" 
Just "johntravolta"

List 作为 Applicative

1
2
3
instance Applicative [] where
pure x = [x]
fs <*> xs = [f x | f <- fs, x <- xs]

list的<*>运算结果是笛卡儿积

1
2
ghci> [(+),(*)] <*> [1,2] <*> [3,4] 
[4,5,5,6,3,4,6,8]

IO 作为 Applicative

1
2
3
4
5
6
instance Applicative IO where
pure = return
a <*> b = do
f <- a
x <- b
return (f x)

没毛病,函数也可以从IO传过来

函数作为Applicative

1
2
3
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)

<*>比较难想出来,可以看下它的参数类型推导:

1
2
3
4
5
6
7
8
9
10
<*> :: f (a -> b) -> f a -> f b
f = ((->) r)
<*> :: ((->) r) (a -> b) -> ((->) r) a -> ((->) r) b
<*> :: (r -> (a -> b)) -> (r -> a) -> (r -> b)
f :: (r -> (a -> b))
g :: (r -> a)
f x :: a -> b
g x :: a
f x (g x) :: b
f <*> g = \x -> f x (g x)
1
2
Prelude> (\x y z -> sum [x, y, z]) <$> (+3) <*> (*3) <*> (/3) $ 99
432.0

挺难理解的,作为课后题。

ZipList

因为list <*> list 是笛卡儿积,我想一对一map怎么办? Haskell 在Control.Applicative里已经定义好了

1
2
3
instance Applicative ZipList where
pure x = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWiht (\f x -> f x) fs xs)

Applicative 定律

1
2
3
4
pure id <*> v = v
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
pure f <*> pure x = pure (f x)
u <*> pure y = pure ($ y) <*> u

LiftA2

定义在Control.Applicative里

1
2
liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = f <$> a <*> b

Applicative 比 普通Functor强在它可以lifting需要N个参数的函数

用它造个轮子:将[f a] 转成 f [a],f 是任意Applicative

1
2
3
4
5
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA x:xs = liftA2 (:) x $ sequenceA xs
--or foldr
sequenceA = foldr (liftA2 (:)) (pure [])