我一直在学习严格与惰性的数据结构,并且一直在使用
:sprint
命令
高铁。我对:sprint
的理解是它显示了所选变量的评估状态。我遇到了以下我无法理解的好奇心。
ghci> data Foo = Foo{i::Int,j::String}
ghci> data Bar = Bar{i:: !Int, j::String}
ghci>
ghci>
ghci> a = Foo (3+2) "abc"
ghci> b = Bar (3+2) "abc"
ghci>
ghci> :sprint a
a = <Foo> _ _
ghci> :sprint b
b = _
我的问题是:为什么
a
默认评估为WHNF,但b
仍然是一个thunk?
我期待
b
的输出是b = <Bar> 5 _
,我可以通过运行seq b ()
来强制。
ghci> seq a ()
()
ghci> seq b ()
()
ghci> :sprint a
a = <Foo> _ _
ghci> :sprint b
b = <Bar> 5 _
我相信这是因为严格的字段只是语法糖,告诉编译器在某些地方自动插入对
seq
的调用。
所以
Bar
上的严格注释意味着 b = Bar (3+2) "abc"
实际上被编译为类似 b = let x = 3+2 in seq x (Bar x "abc")
.
在
a = Foo (3+2) "abc"
之后,a
是对构造函数Foo
的应用的引用;它的字段包含 thunk。构造函数被特殊对待,所以 GHCi 的 :sprint
可以看出 a
指的是构造函数应用程序并将其显示为 a = <Foo> _ _
.
但是在
b = Bar (3+2) "abc"
之后,b
是对seq
的应用的引用,而不是直接对构造函数Bar
的应用。 seq
只是一个函数;它在实现方面很特别,但在内存中以构造函数的方式进行特殊表示方面并不特殊。对(非构造函数)函数应用程序的引用只是一个 thunk,因此 GHCi 将其显示为任何其他 thunk:b = _
.
Forcing
b
引用的 thunk 将强制 3 + 2
然后导致对 Bar
构造函数的应用程序的引用。但是绑定变量不会自动强制分配给它的表达式。