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

Cla*_*bel 92 haskell

无论我已经尝试使用map,fmap已工作过.为什么Haskell的创建者觉得需要一个map函数?难道它不仅仅是目前已知的fmap并且fmap可以从语言中删除吗?

luq*_*qui 89

我想回答一下提请注意奥古斯都的评论:

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

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

  • 而且我还想进一步指出,对于任何尚未意识到的人来说,这个威严是Lennart Augustsson,从Haskell存在以来,他一直是Haskell社区的一部分,参见.[哈斯克尔的历史](http://research.microsoft.com/en-us/um/people/simonpj/papers/history-of-haskell/index.htm),所以有问题的评论不以任何方式二手道听途说! (33认同)
  • 这些后向步骤是否在任何地方收集并记录 看看其他什么被认为是后退步骤以及是否有更好的解决方案将会很有趣. (15认同)
  • map和fmap已经存在很长时间了-在2006年8月的Haskell-prime邮件列表中重新进行了加热-http://www.haskell.org/pipermail/haskell-prime/2006-August/thread。 html。相对而言,我更喜欢现状。在我看来,Haskell的一个子集与Miranda大致对应似乎很有价值。在英国,Miranda被用作数学系学生的教学语言,而不仅仅是计算机科学系的学生。如果这种利基尚未被非功能性语言(例如Mathematica)所迷失,那么我看不到Haskell带有统一的“地图”来填充它。 (3认同)
  • 现在在Haskell wiki上有[Nitpicks页面](https://wiki.haskell.org/Nitpicks),其中提到了这个问题. (3认同)
  • 有趣的是,这个决定是出于教学原因做出的,因为我在学习 Haskell 时一直将 fmap 与 flatmap 混淆。他们应该一起成立一个 n00b 焦点小组。:) (2认同)

And*_*tan 27

引自https://wiki.haskell.org/Typeclassopedia#FunctorFunctor文档

你可能会问为什么我们需要一个单独的map功能.为什么不取消当前的仅列表map功能, 而是重命名fmapmap?嗯,这是一个很好的问题.通常的论点是,只是学习Haskell的人,在使用map不正确时,更愿意看到关于列表的错误而不是关于Functor.


Pao*_*tti 17

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

运行GHCI会话(Glasgow Haskell Compiler Interactive)来查询有关这两个函数的信息,然后查看它们的实现,您将发现许多差异.

地图

查询GHCI了解相关信息 map

Prelude> :info map
map :: (a -> b) -> [a] -> [b]   -- Defined in ‘GHC.Base’
Run Code Online (Sandbox Code Playgroud)

并且您将看到它被定义为适用于任何类型的值列表的高阶函数,a从而产生任何类型的值列表b.虽然多晶型(在ab在上面的定义代表任何类型)的map函数旨在被应用到的值的列表,其是在许多其他之中Haskell中只是一个可能的数据类型.该map函数无法应用于不是值列表的内容.

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

map _ []     = []
map f (x:xs) = f x : map f xs
Run Code Online (Sandbox Code Playgroud)

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

FMAP

现在尝试查询有关的信息fmap,你会看到一些完全不同的东西.

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

此时间fmap被定义为其实现必须由希望属于Functor类型类的那些数据类型提供的函数之一.这意味着可以有多种数据类型,而不仅仅是"值列表"数据类型,能够为fmap函数提供实现.这fmap适用于更大的数据类型集:真正的函子!

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

instance  Functor Maybe  where
  fmap _ Nothing       = Nothing
  fmap f (Just a)      = Just (f a)
Run Code Online (Sandbox Code Playgroud)

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

instance Functor ((,) a) where
  fmap f (x,y) = (x, f y)
Run Code Online (Sandbox Code Playgroud)

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

instance  Functor []  where
  fmap f xs = map f xs
Run Code Online (Sandbox Code Playgroud)

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

结论

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

map  (+3) [1..5]
fmap (+3) (Just 15)
fmap (+3) (5, 7)
Run Code Online (Sandbox Code Playgroud)