为什么seq不好?

问题描述 投票:55回答:2

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

我读过几个消息来源[不是我记得他们现在是谁......]声称“多态性seq不好”。他们以什么方式“坏”?

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

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

haskell lazy-evaluation
2个回答
52
投票

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

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

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

说实话,它可以发明元素,因为它可以将非终止计算/运行时错误“插入”到列表中,因为错误的类型是多态的。也就是说,这种相等性已经在像Haskell这样没有seq的编程语言中破裂了。以下函数定义提供了等式的反例。基本上,在左侧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类型的a -> a运算符是如何工作的。 seq的线索是,如果另一个表达式被评估为正常形式,它会将表达式计算为正常形式。如果seq具有类型a -> a,则无法使一个表达式的评估取决于另一个表达式的评估。


8
投票

this answer中给出了另一个反例 - monads不能满足sequndefined的monad定律。由于undefined无法用图灵完整的语言来避免,因此责怪的是seq

© www.soinside.com 2019 - 2024. All rights reserved.