Haskell'中的惰性评估;使用trace函数的dos表示法
我想知道为什么这个“调试消息1”没有打印在这个代码段中:Haskell'中的惰性评估;使用trace函数的dos表示法,haskell,lazy-evaluation,Haskell,Lazy Evaluation,我想知道为什么这个“调试消息1”没有打印在这个代码段中: import Debug.Trace main = do return (trace "debug message 1" ()) trace "debug message 2" (return ()) 打印第二条“调试消息2”,但不打印“调试消息1”。看来这两种表达方式是一样的 我尝试将“调试消息1”绑定到一个变量,然后在另一个地方使用该变量,它实际上触发了计算并打印“调试消息1”,但我仍然不理解为什么会发生这种情况
import Debug.Trace
main = do
return (trace "debug message 1" ())
trace "debug message 2" (return ())
打印第二条“调试消息2”,但不打印“调试消息1”。看来这两种表达方式是一样的
我尝试将“调试消息1”绑定到一个变量,然后在另一个地方使用该变量,它实际上触发了计算并打印“调试消息1”,但我仍然不理解为什么会发生这种情况
如果我翻转语句的顺序,结果仍然相同:
import Debug.Trace
main = do
trace "debug message 2" (return ())
return (trace "debug message 1" ())
“调试消息1”从未打印(使用runhaskell)。我猜可能是因为“惰性评估” 请注意,您不会返回任何内容。换句话说,“返回”还没有被查询(没有返回),也没有用。在
return
语句中,您不在“一元”上下文中。因此没有理由对其进行评估,只需传递“调用树”作为结果
换言之,它会一直保留在“呼叫树”上,直到有人想接它
对于第二种情况,调用跟踪
很简单。执行monad直到它到达一个“返回
”,在到达该返回之前,将采取所有必要的操作,包括执行调试信息(如果需要)
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
。如果您编写这两个程序:
程序1.hs
:
import Debug.Trace
main = do return (trace "debug message 1" ())
import Debug.Trace
main = do
trace "debug message 2" (return ())
程序2.hs
:
import Debug.Trace
main = do return (trace "debug message 1" ())
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
但是,如果您省略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
do
符号没有什么特别的魔力
main = do
return (trace "debug message 1" ())
trace "debug message 2" (return ())
就跟
main = return (trace "debug message 1" ()) >>=
\_ -> trace "debug message 2" (return ())
main = trace "debug message 2" (return ()) >>=
(\_ -> return (trace "debug message 1" ()))
根据其中一个单子身份法则,返回a>=f=fa
,所以
main = (\_ -> trace "debug message 2" (return ()))
(trace "debug message 1" ())
函数忽略其参数,因此不计算参数;表达式简化为
main = trace "debug message 2" (return ())
第一条消息已完全消失,您可以看到剩余的跟踪
现在是最外层的应用程序,必须对其进行缩减以计算main
,因此将打印此消息
当你翻动订单时,你得到了
main = do
trace "debug message 2" (return ())
return (trace "debug message 1" ())
这和
main = return (trace "debug message 1" ()) >>=
\_ -> trace "debug message 2" (return ())
main = trace "debug message 2" (return ()) >>=
(\_ -> return (trace "debug message 1" ()))
这里的情况有点复杂。第一个
跟踪
(消息2)是强制的,因为>=
对于IO
的左操作数是严格的。然后执行return()
,什么也不做。忽略其值,并执行最后的操作return(跟踪“调试消息1”()
)。这也不做任何事情(return
从不做任何有趣的事情)。由于main
操作的结束是程序的结束,因此不会检查此返回值,因此也不会强制执行此返回值,因此不会对其进行计算。有些人认为应该要求main
具有类型IO()
,以强调其返回值从未被使用。(我相信他们在这方面是错的,因为永远运行的程序应该有类型iovoid
或ioa
,但这是一个挑剔。)如果我颠倒顺序,同样的事情也会发生main=do跟踪“调试消息2”(返回());return(trace“debug message 1”(跟踪“debug message 1”)
@cmcdragokai:如果我在ghci
中运行它,第二次,它将打印调试消息。在此上下文中不打印它的原因是因为它放在return语句之后。但是,如果您执行do trace“debug message 2”(return())
,它将打印调试消息。请使用runhaskell
运行它。更清楚的是,当我使用runhaskell
运行它时,无论主语句的顺序如何,“debug message 1”从未打印过。@cmcdragokai:嗯ghci
或多或少是runhaskell
的等价物,但是是交互式的。如您所见,将打印第二条调试消息。但是很明显,您不能将它放在return
语句之后。您可以在其中添加一个引用,上面写着“当程序执行时,将执行主计算,并丢弃其结果(t类型)”。回答得不错。那么,如果main的类型不是(你的结论暗示)IO(),那么它是什么呢?@cmcdragokai,对于任何a
,它都可以是ioa
。不管是什么,这一结果永远不会被要求。