什么是Functor
functor 就是可以执行map操作的对象,functor就像是附加了语义的表达式,可以用盒子进行比喻。functor 的定义可以这样理解:给出a映射到b的函数和装了a的盒子,结果会返回装了b的盒子。fmap 可以看作是一个接受一个function 和一个 functor 的函数,它把function 应用到 functor 的每一个元素(映射)。
-- Functor的定义 |
某个类型要能进行映射操作(map over),就必须继承Functor基类,并实现其中的fmap函数。我们来看一下几种默认的Functor形态:
列表list,非常好理解,操作列表我们一般使用map函数,它其实就是fmap针对列表的一个具体实例,在list中它们是等价的。
-- 作为functor 的定义:
instance Functor [] where
fmap = map
-- 实例
fmap (*2) [1,2,3]
> [2,4,6]Maybe,它是haskell中使用很广泛的数据类型,它有 Just 值 和 Nothing 两种情况,分别用于表示成功和失败的情况。
-- Maybe 的 functor 定义:
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
-- 实例
fmap (*2) (Just 1)
> Just 2
fmap (*2) (Nothing)
> NothingIO,输入与输出,比如读取键盘输入,打印字符串等
-- IO 的 Functor 定义
instance Functor IO where
fmap f action = do
result <- action
return (f result)
-- 实例
fmap ("hello! "++) getLine
jeff -- 输入名字,打印时添加“hello”
> "hello! jeff"
Functor的 (->) r 形态
(->) r 其实表示的是函数结合,也就是等价于 (.)
-- 下面两个定义是等价的,也就是 (->) r 形式下的 Functor 其实等价于 结合律 |
functor law
如果某个类型遵守这两个定律,那么它与其他Functor对于映射方面就具有相同的性质。
fmap id = id
如果我们对 functor 做 map id,那得到的新的 functor 应该要跟原来的一样fmap id (Just 3)
> Just 3
id (Just 3)
> Just 3fmap (f . g) = fmap f . fmap g 也就是 functor 是能应用于函数结合的。
Applicative Functor
为什么需要 Applicative Functor,什么情况下使用它。从Functor定义我们知道,fmap函数只能映射单个盒子,但假设需要映射两个三个,甚至是更多的盒子呢?或者是要处理返回值是函数的盒子呢?而这就是Applicative Functor 要处理的情况。
Applicative Functor* 可以看作是Functor的增加版,从定义可知,它主要包括pure 和 <\>两个函数。
-- Applicative Functor 定义 |
pure :: a -> f a 意思就是把普通值放到默认的context(语义)下。比如如果是list,那么它代表的就是[ ] ,如果是Maybe,那么它就是 Just 值 / Nothing。
(<*>) 接受一个装有函数的 functor 跟另一个 functor, 非常类似于fmap,它就像加强版的 fmap。以applicative style 的方式来使用 applicative functors。像是 pure f <*> x <*> y <*> … 这个函数可以吃任意多的参数。
-- 与fmap类型的对比,可以看出函数 a -> b 被装进了盒子 f 中
(<*>) :: f (a -> b) -> f a -> f b
fmap :: (a -> b) -> f a -> f b
-- <*> 是左结合的,因此以下两个表达式是相等的
pure (+) <*> Just 3 <*> Just 5
(pure (+) <*> Just 3) <*> Just 5。(<$>) 是applicative functor 中另一个很常用的符号,它其实就是中缀版的fmap。因为结合fmap写applicative functor更加方便。
(<$>) :: (Functor f) => (a -> b) -> f a -> f b
f <$> x = fmap f x
-- 用<*>实现相同的功能
pure f <*> x = fmap f x
接着看一下几个默认的 applicative functor,继承Applicative,必须实现 pure 和 (<*>) 函数
Maybe 类型
-- Maybe 的 Applicative 定义:
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
-- 实例
pure (+3) <*> Just 9
> Just 12
pure (+) <*> Just 3 <*> Just 5
> Just 8列表list 也是 applicative functor,从定义可以看出使用list的Applicative style完全可以实现 list comprehension 的功能。所以 Applicative style 对于 list 而言是取代某些类型的 list comprehension 的好方式。
-- list 的定义
instance Applicative [] where
pure x = [x]
fs <*> xs = [f x | f <- fs, x <- xs]
-- 实例
[(+3),(*2)] <*> [1,2]
> [4,5,2,4]
--下面表达式具有相同的功能
(*) <$> [2,5,10] <*> [8,10,11] -- Applicative style
[ x * y | x <- [2,5,10], y <- [8,10,11]] -- list comprehension
> [16,20,22,40,50,55,80,100,110]IO ,下面的IO的实例,可以把 getLine 看做是一个去真实世界中拿取字串的盒子, 而 applicative functor 表达式会创造一个比较大的盒子,这个大盒子会派两个盒子去终端拿取字串,并把结果串接起来放进自己的盒子中。
--IO 的 Applicative instance
instance Applicative IO where
pure = return
a <*> b = do
f <- a
x <- b
return (f x)
-- 实例 将输入的两个字符串合并
(++) <$> getLine <*> getLine
aa
bb
> "aabb"
Applicative Functor 的 (->) r 形态
(->) r 形态定义
instance Applicative ((->) r) where |
- 用 pure 将一个值包成 applicative functor 的时候,他产生的结果永远都会是那个值
- 将两个 applicative functor 喂给 <*> 可以产生一个新的 applicative functor
接着综合使用上面的知识,来看一下实际应用applicative的几种方式。相比起functor,applicative functor要更强大和灵活。
-- 左结合形式, 第一项必须为含有函数的functor,右边全部为functor |
Applicative Functor 辅助函数
liftA2
只是applicative的套用函数而已,当然还有3个参数的版本 liftA3,而 liftA 则等价于 fmap-- 与applicative 的等价形式
liftA2 f a b = f <$> a <*> b
-- 以下表达式功能一致
liftA2 (:) (Just 3) (Just [4])
(:) <$> Just 3 <*> Just [4]
pure (:) <*> Just 3 <*> Just [4]
> Just [3,4]sequenceA
当套用在函数上时,sequenceA 接受装有一堆函数的list,并回传一个回传list的函数。当我们有一串函数,想要将相同输入都喂给它们并查看结果的时候,sequenceA非常好用。当使用在 I/O action 上的时候,sequenceA 跟 sequence 是等价的。他接受一串 I/O action 并回传一个 I/O action,这个 I/O action 会计算 list 中的每一个 I/O action,并把结果放在一个 list 中
-- 以下是两种实现sequenceA功能一致的函数
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
sequenceA = foldr (liftA2 (:)) (pure [])
sequenceA [Just 3, Just 2, Just 1]
> Just [3,2,1]
-- 将list组合成所有可能的组合
sequenceA [[1,2,3],[4,5,6]]
> [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
sequenceA [(>4),(<10),odd] 7
map (\f -> f 7) [(>4),(<10),odd]
> [True,True,True]
-- and接受一串Bool,并在所有值都为True时才返回True
and $ sequenceA [(>4),(<10),odd] 7
and $ map (\f -> f 7) [(>4),(<10),odd]
> True