带严格字段的Haskell数据结构为何未默认求值为WHNF?
为什么惰性数据结构Foo默认到WHNF,而带严格字段的Bar仍为thunk?
先看你给出的GHCI交互示例:
ghci> data Foo = Foo{i::Int,j::String} ghci> data Bar = Bar{i:: !Int, j::String} ghci> a = Foo (3+2) "abc" ghci> b = Bar (3+2) "abc" ghci> :sprint a a = <Foo> _ _ ghci> :sprint b b = _
核心原因在于Haskell构造函数的WHNF(弱头范式)求值规则,以及GHCI的默认行为:
- WHNF的定义:对于代数数据类型,WHNF意味着构造函数本身已经被求值出来,但构造函数的字段可以还是未求值的thunk。
- 惰性构造函数(Foo)的求值逻辑:
Foo的字段都是惰性的,所以Foo (3+2) "abc"这个表达式的WHNF就是Foo _ _——不需要计算任何字段的值,只需要把构造函数"露出来"就行。GHCI在绑定变量时,会自动把这种低开销的表达式求值到WHNF,所以a的状态是<Foo> _ _。 - 带严格字段的构造函数(Bar)的求值逻辑:
Bar的第一个字段i被标记为严格(!Int),这意味着:要把Bar值求值到WHNF,必须先把严格字段i求值到WHNF。也就是说,要得到Bar (3+2) "abc"的WHNF,必须先计算3+2得到5,这个过程有实际的计算开销,GHCI不会自动触发这个计算,所以b一开始只是一个未求值的thunk(显示为_)。
再看你用seq验证的结果:
ghci> seq a () () ghci> seq b () () ghci> :sprint a a = <Foo> _ _ ghci> :sprint b b = <Bar> 5 _
seq a ():a已经是WHNF了,seq不会做额外求值,所以a的状态不变。seq b ():seq会强制把b求值到WHNF,此时必须先计算严格字段i的3+2得到5,所以b变成<Bar> 5 _,而j字段因为是惰性的,仍然是thunk。
内容的提问来源于stack exchange,提问作者simmysilver
相关产品推荐
相关产品推荐

