为啥我们有map、fmap和liftM?

Posted

技术标签:

【中文标题】为啥我们有map、fmap和liftM?【英文标题】:Why do we have map, fmap and liftM?为什么我们有map、fmap和liftM? 【发布时间】:2011-11-19 18:44:18 【问题描述】:
map :: (a -> b) -> [a] -> [b]

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

liftM :: Monad m => (a -> b) -> m a -> m b

为什么我们有三个不同的函数来做本质上相同的事情?

【问题讨论】:

历史,主要是。 fmap 因教学原因而与 map 不同,liftM 因历史原因与 fmap 不同(即 Functor 不是 Monad 的超类) 哦,为了清楚起见:他们不会“基本上”做同样的事情。 mapliftM 都应该完全fmap做同样的事情。 虽然fmapliftM 做同样的事情,map 当然只是它们的一个特例,即不同的东西。 fmap id getLine 的类型很好,而map id getLine 不是。 【参考方案1】:

map 的存在是为了简化对列表的操作以及出于历史原因(请参阅What's the point of map in Haskell, when there is fmap?)。

您可能会问为什么我们需要一个单独的地图功能。为什么不直接取消当前 仅列表映射函数,并将 fmap 重命名为映射?嗯,这是个好问题。这 通常的论点是,刚学习 Haskell 的人,如果不正确地使用 map,会 而不是看到关于列表的错误而不是关于 Functor 的错误。

--Typeclassopedia,第 20 页

fmapliftM 存在是因为 monad 在 Haskell 中不是自动函子:

我们同时拥有 fmap 和 liftM 的事实是 Monad 类型类不需要的事实的不幸后果 一个 Functor 实例,即使从数学上讲,每个 monad 都是 函子。然而, fmap 和 liftM 本质上是可以互换的,因为它是 任何类型作为实例的错误(在社会而非技术意义上) Monad 也不是 Functor 的一个实例。

--Typeclassopedia,第 33 页

编辑:agustuss 的mapfmap 的历史:

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

-- What's the point of map in Haskell, when there is fmap?

【讨论】:

而且,从我的角度来看,作为在 @augustss 所描述的变化发生十多年后第一次遇到 Haskell 的人,并且花了很多时间帮助现在正在学习语言的人,这不是很明显,它甚至在任何方面都有帮助。当然不足以抵消无用的冗余(这本身就会导致人们提出这样的问题); Functor 类太常见了,不容忽视,不管怎样,初学者经常被错误信息弄糊涂! 我们不能删除liftM吗?让代码破解,谁在乎呢,代码在 github 上修复然后上传到 hackage 通常需要不到 2 天的时间。还是我狂野而疯狂? @Tarrasch:不是每个人都使用 github,也不是所有的包都有按时更新的良好记录,我倾向于在 do-block 中使用 liftM 而不是 @ 987654337@ 因为它更适合我使用liftM2 等。 @L01man 人们已经在这方面工作了;请参阅***.com/questions/5730270/…,至少对于数字类,还有其他选择:hackage.haskell.org/packages/archive/numeric-prelude/0.3.0.2/… @L01man 是的,这很快就会得到解决。 Applicative Monad Proposal (AMP) 看起来会传递到下一个版本的 Haskell 中。 GHC 7.8.3 有一个新标志 --fwarn-amp 以帮助更新现有代码以进行转换。

以上是关于为啥我们有map、fmap和liftM?的主要内容,如果未能解决你的问题,请参考以下文章

“liftM”这个名字的灵感来自数学中的电梯吗? [关闭]

困惑(fmap length Just)[1,1,1,1] vs. fmap length $ Just [1,1,1,1]

为啥我们应该在 Angular 中使用 subscribe() 而不是 map()?

在haskell中拆分使用和不使用fmap的输入读取的行

为啥我们不能在 Array.map() 中使用扩展运算符,还有啥可以替代展平数组? [复制]

Haskell - 树的fmap和foldMap