ghci 中的“trace”函数不会在第二次调用时打印出来
`trace` function in ghci does not print on second invocation
问题:
trace 函数在第二次调用时不起作用,但这只有在包含 trace 的函数被加载到 ghci 时才会发生。
问题
- 为什么会这样?
- 我如何加载 Haskel 模块并仍然具有预期的行为?
意外行为:
我有一个名为 test.hs
的文件
import Debug.Trace (trace)
main = trace "test" return "main is called"
然后我在 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"
预期行为
但是,如果我在 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)
Haskell是懒惰;函数在调用之前不会计算它们的参数,所以一个天真的方法是:
(2 + 2) * (2 + 2)
但这会使工作加倍!
代替的是:
*
/ \
\ /
(2+2)
也就是说,运行时记住 2 + 2
来自一个地方,并且在对其求值时,结果会在表达式的其他部分重用:
*
/ \
\ /
4
然后
16
trace
函数包装了一个表达式,并且仅在表达式为 "popped" 的 第一次 时打印消息。如果再次请求结果,它不会再次打印消息:
ghci> (\x -> x * x) (trace "pop!" (2 + 2))
pop!
16
拼图的下一部分是 IO
动作。在 Haskell 中,像 IO
动作这样的类似语句的东西实际上是像其他任何东西一样的值。它们可以作为参数传递给函数,也可以从函数返回。只是运行时将它们解释为对在现实世界中执行的有效操作的描述。
因此,类型为 IO something
的表达式必须在 执行 之前 求值 (在纯粹的惰性意义上) (在 "interacts with the world" 意义上)。例如:
(\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中的值。类型为函数的表达式必须在调用函数之前求值。
此外,在您的情况下,发生的情况是 main
操作被评估一次,并执行多次。
请注意 trace
的效果(在控制台上打印内容)在 IO
操作的正常流程之外。它们根据惰性评估的变幻莫测而发生。这是一个调试功能,不应该用于 "real" 工作。
更新:我忘了回答这部分问题:为什么 trace
在你的 ghci 实验中有你期望的行为?
trace "test" (return "foo")
是一个多态值,它实际上并没有指定具体的 monad(尝试 :t return "foo"
)。 ghci 的 defaulting rules 在执行之前将其实例化为 IO
。这种多态性使得 ghci 在您每次输入 main
时重新计算该值。如果您提供显式类型注释,如 main = (trace "test" (return "foo")) :: IO String
.
,效果应该会消失
问题:
trace 函数在第二次调用时不起作用,但这只有在包含 trace 的函数被加载到 ghci 时才会发生。
问题
- 为什么会这样?
- 我如何加载 Haskel 模块并仍然具有预期的行为?
意外行为:
我有一个名为 test.hs
import Debug.Trace (trace)
main = trace "test" return "main is called"
然后我在 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"
预期行为
但是,如果我在 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)
Haskell是懒惰;函数在调用之前不会计算它们的参数,所以一个天真的方法是:
(2 + 2) * (2 + 2)
但这会使工作加倍!
代替的是:
*
/ \
\ /
(2+2)
也就是说,运行时记住 2 + 2
来自一个地方,并且在对其求值时,结果会在表达式的其他部分重用:
*
/ \
\ /
4
然后
16
trace
函数包装了一个表达式,并且仅在表达式为 "popped" 的 第一次 时打印消息。如果再次请求结果,它不会再次打印消息:
ghci> (\x -> x * x) (trace "pop!" (2 + 2))
pop!
16
拼图的下一部分是 IO
动作。在 Haskell 中,像 IO
动作这样的类似语句的东西实际上是像其他任何东西一样的值。它们可以作为参数传递给函数,也可以从函数返回。只是运行时将它们解释为对在现实世界中执行的有效操作的描述。
因此,类型为 IO something
的表达式必须在 执行 之前 求值 (在纯粹的惰性意义上) (在 "interacts with the world" 意义上)。例如:
(\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中的值。类型为函数的表达式必须在调用函数之前求值。
此外,在您的情况下,发生的情况是 main
操作被评估一次,并执行多次。
请注意 trace
的效果(在控制台上打印内容)在 IO
操作的正常流程之外。它们根据惰性评估的变幻莫测而发生。这是一个调试功能,不应该用于 "real" 工作。
更新:我忘了回答这部分问题:为什么 trace
在你的 ghci 实验中有你期望的行为?
trace "test" (return "foo")
是一个多态值,它实际上并没有指定具体的 monad(尝试 :t return "foo"
)。 ghci 的 defaulting rules 在执行之前将其实例化为 IO
。这种多态性使得 ghci 在您每次输入 main
时重新计算该值。如果您提供显式类型注释,如 main = (trace "test" (return "foo")) :: IO String
.