我已经检查了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]
答案 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 ()
类型的函数一起使用,例如print
或putStrLn
。这些函数返回()
表示只有效果很重要。如果您使用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