美文网首页程序员
Applicative 函子

Applicative 函子

作者: Sheepy | 来源:发表于2017-02-26 15:42 被阅读151次

Applicative 定律

Application 函子是一种加强的函子,在 Haskell 的 Control.Applicative 模块中定义了一个 Applicative 类型类:

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

同普通函子一样,一个类型构造器要成为 Applicative 的实例的话,它的 kind 必须是* -> *,即接受一个参数,返回一个具体类型。要成为 Applicative 类型类的实例,还必须定义两个函数,pure<*>。从这个定义来看,似乎只要是满足了以上几个条件的类型就可以称为 applicative 函子,事实上并非如此,要成为 applicative 函子还需要满足一条最重要的定律:

pure f <*> x = fmap f x

applicative 函子的用途很明确,就是为了取出第一个函子值中的函数,应用到第二个函子值的值上,上述定律基本可以保证<*>只是做了这件事,当然其他还有一些定律,就不细说了,列在这里大家看看就好:

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

作为 applicative 的函数

我们知道函数可以作为函子,其实函数也可以作为 applicative:

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

这个可能稍微难理解一些,pure 取一个值,产生一个最小上下文,组合成一个 applicative 值,所以产生了一个忽略参数永远返回初始值(pure 的参数)的函数。至于<*>的话,先考虑函数作为普通函子的情况,我们知道函子值是一个包涵上下文的值,当函数作为函子值时,从这个上下文中取值的操作就是将一个参数传递给该函数,然后产生一个值,所以函数作为Functor类型类的实例时是这样的(这种情况下 fmap 其实就是函数组合.):

instance Functor ((->) r) where
  fmap f g = (\x -> f (g x))

我在函子定律中提到过,fmap 接收一个函数和一个函子值,取出函子值中的值传递给函数,然后返回一个函子值。当函数作为函子值时,fmap 还是返回一个函数(这里用 lambda 表示)。g 是函子值,我们要取出它的值,所以给它传递一个参数 x,然后将得到的值作为参数传递给 f,最后将得到的值包裹到 lambda 中(其实整个过程都是在 lambda 中,x 是 lambda 的参数)。那<*>也同理,它接收两个函子值,返回一个函子值,当函数作为函子值时,要先分别取出 f 中的值(函数)和 g 中的值,分别将一个参数 x 传递给它们,再将 g x 作为参数传递给 f x(由于 Haskell 自动柯里化的性质,f x 还是一个函数),最后将结果包裹到 lambda 中。

当然,将((->) r)作为 applicative 使用不是特别重要,但大家要理解并非只有容器或者某种数据结构才能作为函子。

相关文章

网友评论

    本文标题:Applicative 函子

    本文链接:https://www.haomeiwen.com/subject/ezwowttx.html