使用跟踪函数在Haskell的符号中进行惰性求值

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).

dfe*_*uer 7

关于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,但这是一个挑剔.)

  • 您可以添加对https://www.haskell.org/onlinereport/modules.html的引用,其中显示"执行程序时,执行计算主程序,并丢弃其结果(类型为t)". (2认同)

Wil*_*sem 5

我的猜测是因为“惰性评估”。

请注意,您不会返回任何内容。换句话说,“返回”还没有被查询(好吧,没有返回),也没有用。在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)