当有fmap时,Haskell中的地图有什么意义?

时间:2011-07-26 01:17:17

标签: haskell

我尝试过使用map的所有地方,fmap也可以使用。为什么Haskell的创建者觉得需要map函数?难道不能只是当前所谓的fmapfmap可以从语言中移除吗?

3 个答案:

答案 0 :(得分:85)

我想回答引起对augustss's comment的注意:

  

实际上并不是这样的。发生的事情是,地图的类型被概括为涵盖Haskell 1.3中的Functor。即,在Haskell 1.3中,fmap被称为map。然后在Haskell 1.4中恢复此更改并引入了fmap。这种变化的原因是教学法;在向初学者教授Haskell时,非常一般的地图类型使得错误消息更难以理解。在我看来,这不是解决问题的正确方法。

Haskell 98被一些Haskellers(包括我)看作是一个倒退,以前的版本定义了一个更抽象和一致的库。哦,好吧。

答案 1 :(得分:26)

引用https://wiki.haskell.org/Typeclassopedia#Functor

上的Functor文档
  

您可能会问为什么我们需要一个单独的map函数。为什么不这样做呢   离开当前仅限列表的map功能,并将fmap重命名为map   代替?嗯,这是一个很好的问题。通常的论点是   只是学习Haskell的人,在错误地使用map时会有很多   而是看到有关列表的错误,而不是关于Functor

答案 2 :(得分:13)

它们在应用程序网站上看起来一样,但它们当然是不同的。当您将这两个函数mapfmap中的任何一个应用于值列表时,它们将产生相同的结果,但这并不意味着它们的目的是出于同一目的

运行GHCI会话(Glasgow Haskell Compiler Interactive)来查询有关这两个函数的信息,然后查看它们的实现,你会发现很多不同之处。

地图

查询GHCI以获取有关map

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

并且您将其定义为适用于任何类型a的值列表的高阶函数,从而产生任何类型b的值列表。虽然多态(上述定义中的ab代表任何类型),但map函数旨在应用于值列表,这是在Haskell中只有一种可能的数据类型。 map函数无法应用于不是值列表的内容。

正如您可以从GHC.Base源代码中读到的那样,map函数实现如下

map _ []     = []
map f (x:xs) = f x : map f xs

利用模式匹配将头部(x)拉离列表的尾部(xs),然后使用:构建新列表( cons)值构造函数,以便将f x(将其作为" f应用于x" )添加到尾部map的递归,直到列表为止是空的。值得注意的是map函数的实现不依赖于任何其他函数,而只依赖于它自身。

FMAP

现在尝试查询有关fmap的信息,您会看到完全不同的内容。

Prelude> :info fmap
class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b
  ...
  -- Defined in ‘GHC.Base’

此时fmap被定义为其中一个函数必须由希望属于Functor类型类的数据类型提供的函数之一。这意味着可以存在多种数据类型,而不仅仅是"值列表" 数据类型,能够为fmap函数提供实现。这使fmap适用于更大的数据类型集合:函数确实!

正如您可以从GHC.Base源代码中读取的那样,fmap函数的可能实现是Maybe数据类型提供的函数:

instance  Functor Maybe  where
  fmap _ Nothing       = Nothing
  fmap f (Just a)      = Just (f a)

另一种可能的实现是由2元组数据类型提供的实现

instance Functor ((,) a) where
  fmap f (x,y) = (x, f y)

另一种可能的实现是列表数据类型提供的(当然!):

instance  Functor []  where
  fmap f xs = map f xs

依赖于map函数(请注意那里的无点符号......但这超出了原始问题的范围)。

结论

map函数只能应用于值列表(其中值是任何类型),而fmap函数可以应用更多数据类型:所有属于仿函数类(例如maybes,元组,列表等)。由于"值列表" 数据类型也是一个仿函数(因为它为它提供了一个实现),所以fmap可以应用于生成非与map相同的结果。

map  (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)