你并不孤单。 seq
可能是最难的Haskell函数之一,因为几个不同的原因可以正确使用。在你的第一个例子:评估bar q t
之前
foo s t = seq q (bar q t) where
q = s*t
q
进行评估。如果bar q t
从未评估,q
也不会。所以,如果你有
main = do
let val = foo 10 20
return()
为val
是从来没有使用过,也不会进行评估。所以q
也不会被评估。如果你不是有
main = print (foo 10 20)
的foo 10 20
结果(通过print
)评估,因此内foo
q
的bar
结果前进行评估。
这也是为什么这不起作用:
myseq x = seq x x
语义上说,这意味着第二x
评估之前,首先x
进行评估。但如果第二个x
从来没有评估过,第一个也不需要。所以seq x x
完全等同于x
。
你的第二个例子可能是也可能不是相同的东西。这里,表达式s*t
将在bar
的输出之前被评估,但它可能不是与bar
的第一个参数相同的s*t
。如果编译器执行常见的子表达式消除,它可能会共同使用两个相同的表达式。尽管GHC在CSE的哪个位置可以保持相当保守,所以你不能依赖这个。如果我定义了bar q t = q*t
,它在执行CSE并在bar中使用该值之前评估s*t
。对于更复杂的表达式可能不这样做。
您可能还想知道是什么意思严格评估。seq
评估弱头标准形式(WHNF)的第一个参数,对于数据类型来说,这意味着解包最外层的构造函数。试想一下:
baz xs y = seq xs (map (*y) xs)
xs
必须是一个列表,因为map
。当seq
计算它,它基本上将转换代码放入
case xs of
[] -> map (*y) xs
(_:_) -> map (*y) xs
这意味着将确定列表为空或不是,则返回第二个参数。请注意,没有任何列表值被评估为。所以,你可以这样做:
Prelude> seq [undefined] 4
4
,但不是这个
Prelude> seq undefined 5
*** Exception: Prelude.undefined
任何数据类型您使用seq
第一个参数,计算到WHNF将远远不够找出构造并没有进一步。除非数据类型的组件使用爆炸模式标记为严格。然后所有严格的领域也将被评估为WHNF。
编辑:(感谢丹尼尔·瓦格纳在意见建议)
对于函数,seq
将计算表达式,直到功能“有一个拉姆达的表现”,这意味着它已经准备好应用。下面是一些例子,可能表明这意味着什么:
-- ok, lambda is outermost
Prelude> seq (\x -> undefined) 'a'
'a'
-- not ok. Because of the inner seq, `undefined` must be evaluated before
-- the lambda is showing
Prelude> seq (seq undefined (\x -> x)) 'b'
*** Exception: Prelude.undefined
如果你认为一个lambda作为一个(内置),数据构造结合的,seq
上的功能与使用它的数据完全一致。
此外,“lambda绑定”包含所有类型的函数定义,无论是由lambda表示法定义还是作为普通函数定义。
HaskellWiki的seq页面的Controversy部分对seq
与函数有关的一些后果略有介绍。
这可能是有帮助的:http://stackoverflow.com/questions/6872898/haskell-what-is-weak-head-normal-form“它的语义是seq xy意味着每当y被评估为弱头正常形式,x也被评估为弱头正常形式。“ – shang