ghci中的trace函数在第二次调用时不打印

问题描述 投票:1回答:1

问题:

trace函数在第二次调用时不起作用,但这仅在将包含trace的函数加载到ghci中时发生。

问题

  1. 为什么会这样?
  2. 如何加载Haskel模块并仍然具有预期的行为?

意外行为:

我有一个名为test.hs的文件

import Debug.Trace (trace)
main = trace "test" return "main is called"

然后我用ghci输入以下内容。请注意,第二次调用main trace未打印“测试”

Prelude> :l test
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, one module loaded.
*Main> main
test
"main is called"
*Main> main      -- No output from trace?
"main is called"

预期行为

但是,如果我在ghci中键入main函数,则会得到预期的行为

Prelude> import Debug.Trace (trace)
Prelude Debug.Trace> main = trace "test" return "main is called" 
Prelude Debug.Trace> main
test
"main is called"
Prelude Debug.Trace> main -- Output from trace
test
"main is called"
Prelude Debug.Trace> 

更新

@ Robin Zigmond的建议,我尝试了不同的括号,但没有成功

main = trace "test" (return "main is called")
main = return(trace "test" "main is called") -- Joins the outputs into one string
haskell ghci
1个回答
0
投票

首先,是Haskell如何评估事物的一般概念。考虑这个表达式

(\x -> x * x) (2 + 2)

Haskell很懒;函数在调用之前不会评估其参数,因此,执行此操作的一种简单方法是:

(2 + 2) * (2 + 2)

但是您将把工作加倍!

它完成的操作类似于:

    *
   /  \
   \  /
   (2+2)

即,运行时记住2 + 2来自单个位置,并且在对其求值时,结果将在表达式的其他部分重用:

    *
   /  \
   \  /
     4

然后

     16

trace函数包装一个表达式,并且仅在“ [弹出]”时<< [第一次打印消息。如果再次请求结果,它将不会再次打印该消息:

ghci> (\x -> x * x) (trace "pop!" (2 + 2)) pop! 16
难题的下一部分是IO动作。在Haskell中,类似于IO动作的语句之类的东西实际上是与其他任何事物一样的值。它们可以作为参数传递给函数,也可以从函数返回。只是运行时将它们解释为在现实世界中要执行的有效动作的描述。

因此,其类型为IO something的表达式必须在执行之前(在“与世界互动”的意义上)进行评估(在纯粹的惰性意义上)。例如:

(\x -> x >> x) (trace "test" (putStrLn "foo"))

成为

(>>) / \ \ / (trace "test" (putStrLn "foo"))

在这里我们弹出表达式并打印“ test”

(>>) / \ \ / putStrLn "foo"

运行时看到类似

putStrLn "foo", then putStrLn "foo" again

实际上,您写的内容略有不同:(trace "test" return) "main is called"。在您的代码中,trace包装了return函数,而不是结果的IO String值。但是效果相似,函数也是Haskell中的值。类型为函数的表达式必须在调用该函数之前求值。

请注意,trace的效果(在控制台上打印内容)已超出IO动作的正常流程。它们是根据懒惰评估的变化而发生的。这是一个调试功能,不应用于“实际”工作。

© www.soinside.com 2019 - 2024. All rights reserved.