trace 函数在第二次调用时不起作用,但只有在将包含 trace 的函数加载到 ghci 时才会发生这种情况。
我有一个文件叫 test.hs
import Debug.Trace (trace)
main = trace "test" return "main is called"
Run Code Online (Sandbox Code Playgroud)
然后我在 ghci 中输入以下内容。关于第二次调用的注意事项main trace没有打印“test”
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"
Run Code Online (Sandbox Code Playgroud)
但是,如果我在 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>
Run Code Online (Sandbox Code Playgroud)
@Robin Zigmond 建议,我尝试了不同的括号但没有成功
main = trace "test" (return "main is called")
main = return(trace "test" "main is called") -- Joins the outputs into one string
Run Code Online (Sandbox Code Playgroud)
首先,大致了解 Haskell 如何评估事物。考虑这个表达式
(\x -> x * x) (2 + 2)
Run Code Online (Sandbox Code Playgroud)
Haskell 很懒;函数在调用之前不会评估它们的参数,所以这样做的一种天真的方法是:
(2 + 2) * (2 + 2)
Run Code Online (Sandbox Code Playgroud)
但这会使工作加倍!
相反,所做的事情是这样的:
*
/ \
\ /
(2+2)
Run Code Online (Sandbox Code Playgroud)
也就是说,运行时记住它2 + 2来自一个地方,当它被评估时,结果在表达式的其他部分被重用:
*
/ \
\ /
4
Run Code Online (Sandbox Code Playgroud)
进而
16
Run Code Online (Sandbox Code Playgroud)
该trace函数包装一个表达式,并且只在表达式第一次“弹出”时打印消息。如果再次请求结果,它不会再次打印消息:
ghci> (\x -> x * x) (trace "pop!" (2 + 2))
pop!
16
Run Code Online (Sandbox Code Playgroud)
谜题的下一部分是IO行动。在 Haskell 中,像IO动作这样类似语句的东西实际上和其他任何东西一样都是值。它们可以作为参数传递给函数,也可以从函数中返回。只是运行时将它们解释为在现实世界中执行的有效操作的描述。
因此,IO something必须在执行之前(在“与世界交互”的意义上)评估类型为 的表达式(在纯粹的、懒惰的意义上)。例如:
(\x -> x >> x) (trace "test" (putStrLn "foo"))
Run Code Online (Sandbox Code Playgroud)
变成
(>>)
/ \
\ /
(trace "test" (putStrLn "foo"))
Run Code Online (Sandbox Code Playgroud)
在这里,我们弹出表达式并打印一次“test”
(>>)
/ \
\ /
putStrLn "foo"
Run Code Online (Sandbox Code Playgroud)
运行时看到类似
putStrLn "foo", then putStrLn "foo" again
Run Code Online (Sandbox Code Playgroud)
实际上,您写的内容略有不同:(trace "test" return) "main is called". 在您的代码trace中包装return函数,而不是结果IO String值。但是效果是类似的,函数在 Haskell 中也是值。必须在调用函数之前评估类型为函数的表达式。
此外,在您的情况下,发生的情况是该main操作被评估一次,并执行多次。
请注意,trace的效果(在控制台上打印内容)与正常的IO操作流程是带外的。它们是根据惰性求值的变幻莫测而发生的。这是一个不应用于“实际”工作的调试功能。
更新:我忘了回答这部分问题:为什么trace在 ghci 实验中会出现您期望的行为?
trace "test" (return "foo")是一个多态值,它实际上并没有指定一个具体的 monad (try :t return "foo")。ghci 的默认规则会IO在执行之前将其实例化。这种多态性使得 ghci 每次输入时都会重新计算值main。如果您提供显式类型注释,例如main = (trace "test" (return "foo")) :: IO String.