为什么seq不好?

时间:2012-10-02 09:04:18

标签: haskell lazy-evaluation

Haskell有一个名为seq的神奇函数,它接受任何类型的参数并将其缩小为弱头范式(WHNF)。

我已经阅读了几个来源[并不是我能记得他们现在 ...],他们声称“多态seq是坏的”。他们以什么方式“坏”?

同样,还有rnf函数,可以减少 Normal Form (NF)的参数。但是这个是一种类方法;它不适用于任意类型。对我来说似乎“显而易见”可以改变语言规范,将其作为内置基元提供,类似于seq。据推测,这可能比仅仅seq“更糟糕”。这是怎么回事?

最后,有人建议给seqrnfpar和类似id函数的类型,而不是const函数现在,这将是一个进步。怎么样?

2 个答案:

答案 0 :(得分:52)

据我所知,多态seq函数很糟糕,因为它削弱了自由定理,换句话说,没有seq时有效的某些等式不再对seq有效。例如,相等

map g (f xs) = f (map g xs)

适用于所有函数g :: tau -> tau',所有列表xs :: [tau]和所有多态函数f :: [a] -> [a]。基本上,这种相等性表明f只能重新排序其参数列表的元素或删除或复制元素,但不能发明新元素。

老实说,它可以发明元素,因为它可以将非终止计算/运行时错误“插入”到列表中,因为错误的类型是多态的。也就是说,在没有seq的Haskell之类的编程语言中,这种平等已经破裂。以下函数定义提供了等式的反例。基本上,在左侧g“隐藏”错误。

g _ = True
f _ = [undefined]

为了修正等式,g必须严格,也就是说,它必须将错误映射到错误。在这种情况下,平等再次成立。

如果添加多态seq运算符,则等式会再次中断,例如,以下实例化是一个反例。

g True = True
f (x:y:_) = [seq x y]

如果我们考虑列表xs = [False, True],我们有

map g (f [False, True]) = map g [True] = [True]

但另一方面

f (map g [False, True]) = f [undefined, True] = [undefined]

也就是说,您可以使用seq使列表中某个位置的元素取决于列表中另一个元素的定义。如果g是完全的,则平等再次成立。如果您参与自由定理,请查看free theorem generator,它可以指定您是在考虑有错误的语言,还是考虑使用seq的语言。虽然这可能看起来不那么实用,但seq打破了一些用于改善功能程序性能的转换,例如,foldr / build融合在现场失败seq。如果您在seq出现时了解有关自由定理的更多详细信息,请查看Free Theorems in the Presence of seq

据我所知,众所周知,多态seq会破坏某些转换,当它被添加到语言中时。然而,烯化剂也有缺点。如果添加基于seq的类型类,如果在内部某处添加seq,则可能必须向程序添加许多类型类约束。此外,省略seq并不是一个选择,因为已经知道可以使用seq修复空间泄漏。

最后,我可能会遗漏一些内容,但我看不到类型seq的{​​{1}}运算符如何工作。 a -> a的线索是,如果另一个表达式被评估为正常形式,它会将表达式计算为正常形式。如果seq的类型为seq,则无法根据另一个表达式的评估对一个表达式进行评估。

答案 1 :(得分:8)

this answer中给出了另一个反例 - monad无法满足sequndefined的monad法则。由于使用图灵完备语言无法避免使用undefined,因此责怪的是seq

相关问题