为什么seq不好?
Haskell有一个名为seq
的神奇函数,它接受任何types的参数并将其减less为弱头范式 (WHNF)。
我已经读了几个消息来源(不是我现在能记得他们是谁……),声称“多态性seq
是坏的”。 他们以什么方式“坏”?
类似地,还有rnf
函数,它将参数减less到Normal Form (NF)。 但这是一个类方法; 它不适用于任意types。 对我来说,似乎是“显而易见的”,可以改变语言规范,将其作为内置原语提供,类似于seq
。 这大概会比只有seq
更“糟糕”。 这是怎样的?
最后,有人build议给seq
, rnf
, par
和类似于id
函数的types,而不是const
函数,因为它现在是一个改进。 怎么会这样?
据我所知,一个多态seq
函数是不好的,因为它削弱了自由定理,换句话说,没有seq
有效等式在seq
中不再有效。 例如,平等
map g (f xs) = f (map g xs)
对于所有函数g :: tau -> tau'
,所有函数列表xs :: [tau]
和所有多态函数f :: [a] -> [a]
。 基本上,这个平等说明f
只能重新排列其参数列表的元素,或者删除或重复元素,但不能创造新的元素。
说实话,它可以发明元素,因为它可以将非终止计算/运行时错误“插入”列表中,因为错误的types是多态的。 也就是说,这种平等已经在诸如Haskell之类的编程语言中没有seq
。 下面的函数定义为这个方程提供了一个反例。 基本上,在左边g
“隐藏”错误。
g _ = True f _ = [undefined]
为了修正方程, g
必须是严格的,也就是说,它必须将错误映射到错误。 在这种情况下,平等再次成立。
如果添加一个多态seq
运算符,则等式再次中断,例如,以下实例化就是一个反例。
g True = True f (x:y:_) = [seq xy]
如果我们考虑列表xs = [False, True]
,那么我们有
map g (f [False, True]) = map g [True] = [True]
但另一方面
f (map g [False, True]) = f [undefined, True] = [undefined]
也就是说,您可以使用seq
使列表中某个位置的元素依赖于列表中另一个元素的定义。 如果g
是总数,那么等式再次成立。 如果你在自由定理中进行了讨论,请查看免费的定理生成器 ,它允许你指定你是在考虑一个有错误的语言,甚至是一个带有seq
的语言。 虽然这似乎不太实际,但seq
打破了一些用于改进function性程序性能的转换,例如,在seq
存在下, foldr
/ build
融合失败。 如果在seq
存在的情况下对自由定理的更多细节进行了讨论,请看seq
中存在的自由定理 。
据我所知,已经知道一个多态seq
打破了一定的转换,当它被添加到语言。 但是,别名也有缺点。 如果你添加一个基于types的seq
,你可能需要在你的程序中添加大量的types约束,如果你在一个深层的地方添加一个seq
。 此外,没有select省略seq
,因为已经知道可以使用seq
来修复空间泄漏。
最后,我可能会错过一些东西,但我不明白typesa -> a
的seq
运算符是如何工作的。 seq
的线索是,如果另一个expression式被评估为正常forms,它将评估一个expression式以标准forms。 如果seq
已经键入a -> a
那么就不能根据另一个expression式的评估来对一个expression式进行评估。
在这个答案中给出了另一个反例 – monad不能满足seq
和undefined
单子法则。 而且由于undefined
在图灵完全语言中是不可避免的,所以责备的是seq
。