seq功能和严格性

时间:2012-06-15 07:45:36

标签: haskell strict

我一直在想这个问题,但我一直无法找到它。

使用seq功能时,真正的工作原理是什么?在任何地方,只是解释说seq a b评估a,丢弃结果并返回b

真正意味着什么?以下结果将得到严格评估:

foo s t = seq q (bar q t) where
      q = s*t

我的意思是,qbar中使用之前是否经过严格评估?以下是相同的:

foo s t = seq (s*t) (bar (s*t) t)

我觉得有点难以详细说明这个功能的功能。

2 个答案:

答案 0 :(得分:34)

你并不孤单。由于几个不同的原因,seq可能是最难以正确使用的Haskell函数之一。在你的第一个例子中:

foo s t = seq q (bar q t) where
      q = s*t
在评估q之前评估

bar q t。如果永远不会评估bar q t,则q也不会。所以,如果你有

main = do
    let val = foo 10 20
    return ()

从未使用过val,因此不会对其进行评估。因此,q也不会被评估。如果您改为

main = print (foo 10 20)

评估foo 10 20的结果(print),因此foo qbar myseq x = seq x x 的结果评估。

这也是为什么这不起作用的原因:

x

从语义上讲,这意味着在评估第二个x之前将评估第一个x。但是如果永远不会评估第二个seq x x,那么第一个x也不需要。因此s*t完全等同于bar

你的第二个例子可能是也可能不是同一个例子。在这里,表达式s*t将在bar输出之前进行评估,但它可能与bar q t = q*t的第一个参数不同s*t。如果编译器执行公共子表达式消除,则它可以共同使用两个相同的表达式。 GHC对CSE的位置可能相当保守,所以你不能依赖于此。如果我定义seq,它会执行CSE并在使用该值之前评估baz xs y = seq xs (map (*y) xs) 。对于更复杂的表达式,它可能不会这样做。

您可能还想了解严格评估的含义。 xs计算弱头正规形式(WHNF)的第一个参数,对于数据类型,它意味着解包最外层的构造函数。考虑一下:

map
由于seq

case xs of [] -> map (*y) xs (_:_) -> map (*y) xs 必须是列表。当Prelude> seq [undefined] 4 4 对其进行评估时,它将基本上将代码转换为

Prelude> seq undefined 5
*** Exception: Prelude.undefined

这意味着它将确定列表是否为空,然后返回第二个参数。请注意,没有评估列表值。所以你可以这样做:

seq

但不是这个

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 的第一个参数的数据类型是什么,对WHNF的评估将足以找出构造函数而不会进一步。除非数据类型具有使用爆炸模式标记为严格的组件。然后,所有严格的字段也将被评估为WHNF。

编辑:(感谢Daniel Wagner在评论中提出建议)

对于函数,seq将评估表达式,直到函数“显示lambda”,这意味着它已准备好应用程序。以下是一些可能证明这意味着什么的例子:

seq

如果您将lambda绑定视为(内置)数据构造函数,函数上的{{1}}与在数据上使用它完全一致。

此外,“lambda绑定”包含所有类型的函数定义,无论是由lambda表示法还是作为普通函数定义。

HaskellWiki的seq页面的Controversy部分有一些关于{{1}}与函数相关的一些后果。

答案 1 :(得分:4)

您可以将seq视为:

seq a b = case a of
            _ -> b

这会将a评估为正常格式(WHNF),然后继续评估b

在奥古斯特评论之后进行修改:case ... ofstrict, GHC Core one,它总是强制论证。