阿男的小窝

View the Project on GitHub

Haskell:对Functor的分析

这篇文章里面介绍Functor classFunctor class的定义如下:

class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b
  (<$) :: a -> f b -> f a
  {-# MINIMAL fmap #-}
  	-- Defined in ‘GHC.Base’
instance Functor (Either a) -- Defined in ‘Data.Either’
instance Functor [] -- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘GHC.Base’
instance Functor IO -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
instance Functor ((,) a) -- Defined in ‘GHC.Base’

注意到它的核心就是定义了fmap函数类型:

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

这个fmap类型的定义很清楚,它接受两个参数:

  1. (a -> b)
  2. f a

对上面的定义说明如下:

是不是有点晕?其实是非常清晰明了的一个定义,没关系,接下来看,就很清楚了。

我们接着看看Functor class下面的instance

instance Functor [] -- Defined in ‘GHC.Base’

从上面这个instance,我们知道了,原来列表(就是[]类型的数据)也是Functorinstance,说明列表实现了fmap

那么列表数据是怎样实现fmap的?

答案是:map就是fmap的实现。

我们看下map的定义:

Prelude> :t map
map :: (a -> b) -> [a] -> [b]
Prelude>

实际上mapfmap给实现了,fmap里面的f af b被具体到了[]列表类型。

最后我们看下为什么说map就是fmap的实现。

首先我们下载haskell的源代码:

然后我们确定一下map的位置:

Prelude> :info map
map :: (a -> b) -> [a] -> [b] 	-- Defined in ‘GHC.Base’

从上面的info我们看到map is defined in “GHC.Base”

于是我们验证一下map实现了fmap,首先根据上面的信息,找到正确的分析位置:

$ grep -rl 'fmap =' *  | grep -v test | grep GHC
grep: inplace/test   spaces: No such file or directory
ghc/GHCi/UI/Monad.hs
libraries/base/GHC/Arr.hs
libraries/base/GHC/Base.hs

然后我们打开Base.hs,看到了证据:

以下是map的具体定义:

----------------------------------------------
--              map
----------------------------------------------

-- | 'map' @f xs@ is the list obtained by applying @f@ to each element
-- of @xs@, i.e.,
--
-- > map f [x1, x2, ..., xn] == [f x1, f x2, ..., f xn]
-- > map f [x1, x2, ...] == [f x1, f x2, ...]

map :: (a -> b) -> [a] -> [b]
{-# NOINLINE [0] map #-}
  -- We want the RULEs "map" and "map/coerce" to fire first.
  -- map is recursive, so won't inline anyway,
  -- but saying so is more explicit, and silences warnings
map _ []     = []
map f (x:xs) = f x : map f xs

可以看到map的定义是递归式的。

*小结*

在这篇文章中,给大家介绍了Functor class,明白了这个class的核心是为了定义fmap这个行为。