了解GHCi让绑定的不同行为
我一直在玩Simon Marlow关于Haskell中并行和并发编程的书中的一些例子,并偶然发现了一个我不太了解的有趣的行为。 这实际上是关于我想了解GHC的一些内部工作。
假设我在REPL中执行以下操作:
λ» let x = 1 + 2 :: Int λ» let z = (x,x) λ» :sprint x x = _ λ» :sprint z z = (_,_) λ» seq x () () λ» :sprint z z = (3,3)
好吧,这几乎是我所期望的,除了z已经被评估为WHNF。 让我们写一个类似的程序,并把它放在一个文件中:
module Thunk where import Debug.Trace x :: Int x = trace "add" $ 1 + 2 z :: (Int,Int) z = (x,x)
在GHCi中摆弄它:
λ» :sprint x x = _ λ» :sprint z z = _ λ» seq x () add () λ» :sprint z z = _ λ» seq z () () λ» z (3,3)
所以这个行为有点不同: z
不提前评估WHNF。 我的问题是:
为什么z
在执行let z = (x,x)
时在REPL中评估为WHNF,但是在从文件中加载定义时不会执行let z = (x,x)
。 我的怀疑是它与模式绑定有关,但我不知道该在哪里寻求澄清(也许我完全是完全错误的)。 我会期望它在某种程度上像文件中的例子。
任何指针或简短的解释,为什么发生这种情况?
因为(,)
是一个构造函数,所以它与Haskell的语义没有任何区别( :sprint
允许访问内部的thunk实现细节,因此不计算在内)。所以这是一个GHC在编译时做什么优化和折衷的问题(x,x)
在不同的位置。 其他人可能知道这些情况下的确切原因。