Haskell中mapM_和mapM有什么区别?

时间:2014-12-22 19:18:51

标签: haskell

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

Hoogle说mapM_忽略了结果。

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

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

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

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

4 个答案:

答案 0 :(得分:30)

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

但是,有时我们需要执行副作用建立结果列表。如果我们有一个诸如

之类的功能
lookupUserById :: UserId -> IO User

然后我们可以使用此功能将UserId的列表扩展为User的列表:

lookupUsers :: [UserId] -> IO [User]
lookupUsers = mapM lookupUserById

答案 1 :(得分:10)

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

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

您始终将mapM_a -> m ()类型的函数一起使用,例如printputStrLn。这些函数返回()表示只有效果很重要。如果您使用mapM,则会获得()(即[(), (), ()])的列表,这将完全没用,但会浪费一些内存。如果你使用mapM_,你只会获得(),但它仍会打印所有内容。

另一方面,如果您关心返回的值,请使用mapM。作为一个假设的例子,想象一个函数fetchUrl :: Url -> IO Response - 很有可能,你关心每个URL的响应。因此,为此,您需要使用mapM来获取响应列表,然后您可以在其余代码中使用这些响应。

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

普通map有所不同:它使用普通函数(a -> b)而不是使用monad(a -> m b)的函数。这意味着除了返回更改的列表之外,不能具有任何类型的效果。如果要使用普通函数转换列表,可以使用它。 map_不存在,因为您没有任何效果,始终关心使用map的结果。

答案 2 :(得分:5)

更一般地说,差异在于mapM_只需要"consume"输入的所有元素,而mapM也需要重新构建"数据结构,带有新值。对于列表来说,这几乎是微不足道的:您只需要沿着cons-cells traverse,使用从您映射的操作中获得的值更新值。但对于结构取决于实际包含值的容器而言,它并不容易。所以例如对于Data.Set,您无法定义mapM(a -> m b) -> Set a -> m (Set b)类型的等价物 - 它是Foldable的实例,但不是Traversable的实例

答案 3 :(得分:1)

mapM_忽略结果。这意味着,不会返回任何返回值。您可以在交互模式下看到这一点:

$ ghci
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.

Prelude> mapM (putStrLn . show ) [1,2]
1
2
[(),()]
Prelude> mapM_ (putStrLn . show ) [1,2]
1
2