Haskell学习-functor

原文地址:Haskell学习-functor

什么是Functor

functor 就是可以执行map操作的对象,functor就像是附加了语义的表达式,可以用盒子进行比喻。functor 的定义可以这样理解:给出a映射到b的函数和装了a的盒子,结果会返回装了b的盒子。fmap 可以看作是一个接受一个function 和一个 functor 的函数,它把function 应用到 functor 的每一个元素(映射)。

-- Functor的定义 class Functor f where fmap :: (a -> b) -> f a -> f b

某个类型要能进行映射操作(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) > Nothing

IO,输入与输出,比如读取键盘输入,打印字符串等

-- 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 其实等价于 结合律 instance Functor ((->) r) where fmap f g = (\x -> f (g x)) instance Functor ((->) r) where fmap = (.) -- 实例 fmap (*3) (+100) 1 > 303 (*3) . (+100) $ 1 > 303 functor law

如果某个类型遵守这两个定律,那么它与其他Functor对于映射方面就具有相同的性质。

fmap id = id
如果我们对 functor 做 map id,那得到的新的 functor 应该要跟原来的一样

fmap id (Just 3) > Just 3 id (Just 3) > Just 3

fmap (f . g) = fmap f . fmap g 也就是 functor 是能应用于函数结合的。

Applicative Functor

  为什么需要 Applicative Functor,什么情况下使用它。从Functor定义我们知道,fmap函数只能映射单个盒子,但假设需要映射两个三个,甚至是更多的盒子呢?或者是要处理返回值是函数的盒子呢?而这就是 Applicative Functor 要处理的情况。
  Applicative Functor 可以看作是Functor的增加版,从定义可知,它主要包括pure 和 <*>两个函数。

-- Applicative Functor 定义 class (Functor f) => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b

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]

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssyfg.html