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



问题:

跟踪函数在第二次调用中不起作用,但只有当包含跟踪的函数加载到ghci中时才会发生这种情况。

问题

  1. 为什么会发生这种情况
  2. 如何加载Haskel模块并保持预期的行为

意外行为:

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

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

然后我在ghci中键入以下内容。关于CCD_ 2的第二次调用的注释CCD_;测试";

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中键入主函数,我会得到预期的行为

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如何评估事物的一般概念。考虑这个表达式

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

哈斯克尔很懒;函数在调用之前不会评估它们的参数,所以一种简单的方法是:

(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"))

在这里,我们弹出表达式,"测试"被打印为一次

(>>)
/  
  /
putStrLn "foo"

运行时看到类似的东西

putStrLn "foo", then putStrLn "foo" again

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

此外,在您的案例中,发生的情况是main操作被评估一次,并执行多次。

请注意,trace的效果(在控制台上打印内容(与IO动作的正常流程不在同一范围内。它们是根据懒惰评价的变幻莫测而发生的。这是一个调试函数,不应该用于"实际"工作。


更新:我忘了回答这部分问题:为什么trace在你的ghci实验中具有你期望的行为?

trace "test" (return "foo")是一个多态值,它实际上并没有指定具体的monad(试试:t return "foo"(。ghci的默认规则在执行之前将其实例化为IO。这种多态性使ghci每次输入main0时都会重新计算值。如果提供显式类型注释(如main = (trace "test" (return "foo")) :: IO String(,则效果应该会消失。

最新更新