我想知道为什么这个“调试消息1”没有打印在这个片段中:
import Debug.Trace
main = do
return (trace "debug message 1" ())
trace "debug message 2" (return ())
打印出第二个“调试消息2”,但不打印“调试消息1”。似乎两个表达都是一样的。
我尝试将“debug message 1”绑定到变量,然后在另一个地方使用该变量,它实际上触发了评估并打印“debug message 1”,但我仍然不明白为什么会发生这种情况
如果我翻转语句的顺序,它仍然是相同的结果:
import Debug.Trace
main = do
trace "debug message 2" (return ())
return (trace "debug message 1" ())
永远不会打印“调试消息1”(使用runhaskell)。
答案 0 :(得分:7)
do
符号没有特别的魔力。
main = do
return (trace "debug message 1" ())
trace "debug message 2" (return ())
与
相同main = return (trace "debug message 1" ()) >>=
\_ -> trace "debug message 2" (return ())
根据monad身份法之一,return a >>= f = f a
,所以
main = (\_ -> trace "debug message 2" (return ()))
(trace "debug message 1" ())
该函数忽略其参数,因此不计算参数;表达式减少到
main = trace "debug message 2" (return ())
第一条消息已完全消失,您可以看到剩余的trace
现在是必须缩减以评估main
的最外层应用程序,因此将打印此消息。
当你翻转订单时,你得到了
main = do
trace "debug message 2" (return ())
return (trace "debug message 1" ())
这与
相同main = trace "debug message 2" (return ()) >>=
(\_ -> return (trace "debug message 1" ()))
这里的情况有点复杂。强制第一个trace
(消息2),因为>>=
的{{1}}在其左操作数中是严格的。然后执行IO
,什么都不做。忽略其值,并执行最终操作return ()
。这也没有做任何事情(return (trace "debug message 1" ())
从不做任何有趣的事情)。由于return
操作的结束是程序的结束,因此永远不会检查此返回值,因此从不强制,因此不会对其进行评估。有些人认为main
应该要求main
类型强调它的返回值从未使用过。 (我认为他们错了,因为永远运行的程序应该真的有IO ()
或IO Void
类型,但那是一个挑剔。)
答案 1 :(得分:3)
我的猜测是因为"懒惰评估"。
请注意,您不会返回任何内容。换句话说,"返回"尚未被查询(没有回复),也没有用。在return
声明中,您不在" monadic"上下文。所以没有理由对它进行评估,你只需传递" call-tree"结果。
换句话说,它仍然存在于" 调用树"直到有人想要拿起它。
对于第二种情况,调用trace
是微不足道的。 monad一直执行,直到达到return
",在达到return
之前,将采取所有必要的操作,包括在需要时执行调试信息。
ghci
中的示例:
$ 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> import Debug.Trace
Prelude Debug.Trace> do return (trace "debug message 1" ())
Prelude Debug.Trace> do trace "debug message 2" (return ())
debug message 2
runhaskell
相同。如果你写这两个程序:
<强> program1.hs
强>:
import Debug.Trace
main = do return (trace "debug message 1" ())
<强> program2.hs
强>:
import Debug.Trace
main = do
trace "debug message 2" (return ())
然后控制台显示:
$ runhaskell program1.hs
$ runhaskell program2.hs
debug message 2
$
但是,如果您编写IO Bool
(因此返回值)并稍后使用该值,则将执行跟踪,例如:
testFun :: IO Bool
testFun = do
trace "foo" $ return $ trace "Hello" True
main :: IO ()
main = do
b <- testFun
print b
这将导致:
$ runhaskell program3.hs
foo
Hello
True
$
如果您省略print b
并放置return ()
,Haskell对返回的内容不感兴趣,因此不会打印跟踪:
testFun :: IO Bool
testFun = do
trace "foo" $ return $ trace "Hello" True
main :: IO ()
main = do
b <- testFun
return () --we ran `testFun` but are not interested in the result
结果是:
$ runhaskell program4.hs
foo
$