为什么不能将 print 映射到 Haskell 中的列表?

Why can't you map print to a list in Haskell?

我在做这个之前检查了 this post,但是 post 并没有真正解释 为什么 这不起作用。

foo :: (Num a, Show a) => a -> IO ()
foo 0 = print "Was zero"
foo x = print x

foo 2 -- prints 2
foo 5 -- prints 5

map foo [0..10] :: [IO ()] -- Doesn't print out numbers.

编辑:

bar 0 = foo 0
bar n = do
            foo n
            bar (n - 1)

明白为什么这个returns[IO ()],但我不明白为什么在构建这个列表时不打印。至少,我希望我们会看到 first print 调用发生,因为惰性求值。

在创建这个列表的过程中,为什么没有打印到屏幕的'side effect'? foo 函数在应用于列表的每个元素时是否实际上没有输入?如果评估 print 调用以获得 IO () 来构建列表,为什么没有发生副作用?

IO monad 中,需要像 IO a 这样的东西。你有 [IO ()] 这是一个 IO 操作列表。您可以改用 mapM 来创建单子图。 mapM f 等同于 sequence . map f.

If the print calls are evaluated to get the IO () to build the list, why doesn't the side effect happen?

因为计算 IO () 类型的表达式没有任何副作用。事实上,计算任何类型的表达式都没有任何副作用。

map foo [0..10]肯定会构造一个IO ()值的列表,但是构造一个IO ()值不会执行IO。你可能凭直觉知道这一点:如果我们有一个全局 IO () 并且没有任何引用它,它就不会被执行:

sayHello :: IO ()
sayHello = putStrLn "hello"

您可能将其归因于懒惰;毕竟,如果 sayHello 未被引用,则没有任何东西会强制其求值。但是,这也没有做任何事情:

main = sayHello `seq` return ()

在这里,我们肯定评估sayHello,但我们只评估IO ()

IO 做某事的原因是当你将它组合成另一个 IO(比如 main)并且 that 得到 运行,然后只有这样才会做一些事情:

main = sayHello

我应该注意到 GHCi 使情况有点混乱。 GHCi 与 IO 有一些特殊的交互:如果您在提示符下键入的整个表达式产生一个 IO,它将执行它并显示它的结果。否则,它只显示结果。但这只是一个交互功能;在真正的 Haskell 代码中,你不能只有一个 IO () 并期望它神奇地变成一个 ()

运行 IO 操作的唯一方法是将其分配给 main。评估 IO 个动作不会 运行 它们。

如果你想在值列表上调用print,你必须做两件事:

首先:使用 mapM_ 构建一个 IO 动作,其中 print 每个值:

mapM_ print [1..3] :: IO ()

其次,将该表达式分配给 main:

main = mapM_ print [1..3]

如果您省略第二步(将其分配给 main 以外的其他内容),则不会发生任何事情。