CMC*_*kai 3 haskell lazy-evaluation
我想知道为什么这个"调试消息1"没有打印在这个片段中:
import Debug.Trace
main = do
return (trace "debug message 1" ())
trace "debug message 2" (return ())
Run Code Online (Sandbox Code Playgroud)
打印出第二个"调试消息2",但不打印"调试消息1".似乎两个表达都是一样的.
我尝试将"debug message 1"绑定到变量,然后在另一个地方使用该变量,它实际上触发了评估并打印"debug message 1",但我仍然不明白为什么会发生这种情况.
如果我翻转语句的顺序,它仍然是相同的结果:
import Debug.Trace
main = do
trace "debug message 2" (return ())
return (trace "debug message 1" ())
Run Code Online (Sandbox Code Playgroud)
永远不会打印"调试消息1"(使用runhaskell).
关于do符号没有特别的魔力.
main = do
return (trace "debug message 1" ())
trace "debug message 2" (return ())
Run Code Online (Sandbox Code Playgroud)
就像是一样的
main = return (trace "debug message 1" ()) >>=
\_ -> trace "debug message 2" (return ())
Run Code Online (Sandbox Code Playgroud)
通过monad身份法之一return a >>= f = f a,如此
main = (\_ -> trace "debug message 2" (return ()))
(trace "debug message 1" ())
Run Code Online (Sandbox Code Playgroud)
该函数忽略其参数,因此不评估参数; 表达式减少到
main = trace "debug message 2" (return ())
Run Code Online (Sandbox Code Playgroud)
第一条消息完全消失了,您可以看到剩下trace的消息现在是必须减少评估的最外层应用程序main,因此将打印此消息.
当你翻转订单时,你得到了
main = do
trace "debug message 2" (return ())
return (trace "debug message 1" ())
Run Code Online (Sandbox Code Playgroud)
这是一样的
main = trace "debug message 2" (return ()) >>=
(\_ -> return (trace "debug message 1" ()))
Run Code Online (Sandbox Code Playgroud)
这里的情况有点复杂.第一个trace(消息2)是强制的,因为>>=for IO左操作数是严格的.然后return ()执行,什么都不做.忽略其值,并执行最终操作return (trace "debug message 1" ()).这也什么return 都不做(从来没有做过任何有趣的事).由于main操作结束是程序的结束,因此永远不会检查此返回值,因此从不强制,因此不会对其进行评估.有些人认为main应该要求类型IO ()强调其返回值从未使用过.(我相信他们错了,因为永远运行的程序应该有类型IO Void或者IO a,但这是一个挑剔.)
我的猜测是因为“惰性评估”。
请注意,您不会返回任何内容。换句话说,“返回”还没有被查询(好吧,没有返回),也没有用。在return声明中,您并不处于“monadic”上下文中。因此没有理由评估它,您只需将“调用树”作为结果传递即可。
换句话说,它保留在“调用树”上,直到有人想要接听它。
对于第二种情况,trace调用 will 是微不足道的。执行 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
Run Code Online (Sandbox Code Playgroud)
对于 也一样runhaskell。如果你写这两个程序:
program1.hs:
import Debug.Trace
main = do return (trace "debug message 1" ())
Run Code Online (Sandbox Code Playgroud)
program2.hs:
import Debug.Trace
main = do
trace "debug message 2" (return ())
Run Code Online (Sandbox Code Playgroud)
然后控制台显示:
$ runhaskell program1.hs
$ runhaskell program2.hs
debug message 2
$
Run Code Online (Sandbox Code Playgroud)
但是,如果您编写一个IO Bool(因此具有返回值)并且稍后使用该值,则将执行跟踪,例如:
testFun :: IO Bool
testFun = do
trace "foo" $ return $ trace "Hello" True
main :: IO ()
main = do
b <- testFun
print b
Run Code Online (Sandbox Code Playgroud)
这将导致:
$ runhaskell program3.hs
foo
Hello
True
$
Run Code Online (Sandbox Code Playgroud)
然而,如果您省略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
Run Code Online (Sandbox Code Playgroud)
结果是:
$ runhaskell program4.hs
foo
$
Run Code Online (Sandbox Code Playgroud)