32

我在 Real World Haskell 的 I/O 章节。书中还有 7 章没有讨论 Monad。也就是说,我对 I/O 的理解充其量是不完整的。

现在我正在尝试理解 mapM 函数。据我了解,该函数“执行”列表中必须是“动作”(IO monad)的每个元素。

没有意义的是这个例子。为什么 mapM 对于相同的参数返回与 map 不同的结果?

Prelude> map (\x -> [x]) [0, 1, 2]
[[0],[1],[2]]
前奏> mapM (\x -> [x]) [0, 1, 2]
[[0,1,2]]
4

2 回答 2

21

据我了解,该函数“执行”列表中必须是“动作”(IO monad)的每个元素。

IO 确实如此,但在您的代码示例中,您不使用 IO 单子,而是使用列表单子(您提供给 mapM 的函数返回列表([x]),而不是 IO)。

mapM定义为mapM f as = sequence (map f as)。如果 f 返回一个 IO,这意味着对于列表中的每个元素,它通过将 f 应用于元素来构造一个 IO。然后它将映射返回的 IO 列表转换为使用序列“包含”列表的 IO(因此,当您执行 IO 时,您将返回一个包含非 IO 值的列表)。

对于列表,这意味着它通过应用f到 的每个元素来创建列表列表as。然后它sequence用于创建一个列表列表,其中包含所有可能的方法来获取列表列表中每个列表的一个元素(例如sequence [[1,2],[3,4]]返回[[1,3],[1,4],[2,3],[2,4]])。

于 2010-06-29T01:10:45.430 回答
19

可能值得明确的是,这两个片段不是“类似的”,你不应该期望相关的结果。特别是,“一元”版本的

映射 (\x -> [x]) [0, 1, 2]

mapM (\x -> 返回 [x]) [0, 1, 2]

注意额外的return.

一般来说,return (map f x)与 相同mapM (return . f) x

这是因为对于列表 monad,x >>= f“扁平化”了应用f到的结果x。当您遗漏应用程序return的结果时,结果将\x -> [x]被展平。有一个额外的return抵消了额外的扁平化。

于 2010-06-29T22:38:47.290 回答