Haskell中mapM_和mapM有什么区别?

Gog*_*aka 19 haskell

我已经检查了hoogle,http: //hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html#v:mapM

霍格尔说mapM_忽略了结果.

但我仍然不知道如何正确使用.

main = mapM_ (putStrLn.show) [1,2]

main = mapM (putStrLn.show) [1,2]

main = map (putStrLn.show) [1,2]
Run Code Online (Sandbox Code Playgroud)

och*_*les 32

mapM_对于仅为其副作用执行某些操作非常有用.例如,将字符串打印到标准输出不会返回任何有用的东西 - 它会返回().如果我们有一个包含三个字符串的列表,我们最终会累积一个列表[(), (), ()].构建此列表在速度和内存使用方面都有运行时成本,因此通过使用mapM_我们可以完全跳过此步骤.

但是,有时我们需要执行副作用建立结果列表.如果我们有一个如...的功能

lookupUserById :: UserId -> IO User
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用它来将UserIds列表扩展为Users 列表:

lookupUsers :: [UserId] -> IO [User]
lookupUsers = mapM lookupUserById
Run Code Online (Sandbox Code Playgroud)


Tik*_*vis 12

核心思想是在列表mapM上映射"动作"(即类型函数a -> m b),并将所有结果作为a m [b].mapM_做同样的事情,但从不收集结果,返回一个m ().

如果您关心a -> m b函数的结果(即bs),请使用mapM.如果您只关心效果,无论它是什么,而不是结果值,请使用,mapM_因为它可以更有效,更重要的是,使您的意图清晰.

你总是会使用mapM_类型的函数a -> m (),比如printputStrLn.这些函数返回()表示只有效果很重要.如果你使用过mapM,你会得到一个()(即[(), (), ()])的列表,这将完全没用,但会浪费一些记忆.如果你使用mapM_,你会得到一个(),但它仍然会打印一切.

另一方面,如果您关心返回的值,请使用mapM.作为一个假设的例子,想象一下函数 - fetchUrl :: Url -> IO Response机会,您关心每个URL的响应.因此,为此,您将mapM获得一个响应列表,然后您可以在其余代码中使用它们.

所以:mapM如果你关心结果,mapM_如果你不关心.

Normal map是不同的东西:它使用普通的函数(a -> b)而不是使用monad(a -> m b)的函数.这意味着除了返回更改的列表之外,它不会产生任何影响.如果要使用普通函数转换列表,可以使用它.map_不存在因为,因为你没有任何影响,你总是关心使用的结果map.


lef*_*out 5

更一般地说,区别在于mapM_只需要"消耗"输入的所有元素,而mapM还需要使用新值"重新构建"数据结构.对于列表来说,这几乎是微不足道的:您只需要遍历 cons-cells,使用从您映射的动作接收的值更新值.但是对于结构取决于实际包含值的容器而言,它并不容易.因此,例如,因为Data.Set你不能定义mapM与类型的等价物(a -> m b) -> Set a -> m (Set b)- 它是一个实例Foldable,但不是Traversable