为什么不能将 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
以外的其他内容),则不会发生任何事情。
我在做这个之前检查了 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
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
以外的其他内容),则不会发生任何事情。