61

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

我读过一些资料 [不是我记得他们现在是谁......] 声称“多态seq是坏的”。他们在哪些方面是“坏的”?

类似地,还有一个rnf函数,它可以将参数简化为范式(NF)。但这是一个类方法它不适用于任意类型。对我来说,似乎“很明显”可以更改语言规范以将其作为内置原语提供,类似于seq. 据推测,这将比仅仅拥有seq. 这是怎么回事?

最后,有人建议将seq, rnf,par和类似函数赋予与函数相同的类型id,而不是const现在的函数,这将是一种改进。怎么会这样?

4

2 回答 2

57

据我所知,多态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如果是全数,则等式再次成立。如果您对自由定理感兴趣,请查看自由定理生成器,它允许您指定您是否正在考虑一种有错误的语言,甚至是一种有seq. 虽然,这似乎不太实际相关,但seq会破坏一些用于提高功能程序性能的转换,例如foldr/ buildfusion 在seq. 如果您对存在 seq 的自由定理的更多细节感兴趣,seq请查看存在 seq 的自由定理.

据我所知,当多态seq被添加到语言中时,它会破坏某些转换。然而,替代品也有缺点。如果您添加一个基于类型类的seq,您可能必须在程序中添加大量类型类约束,如果您在seq某个深处添加了一个。此外,省略也不是一个选择,seq因为已经知道存在可以使用seq.

最后,我可能会错过一些东西,但我看不出seq类型运算符是如何a -> a工作的。的线索seq是,如果另一个表达式被评估为头范式,它会将一个表达式评估为头范式。如果seq具有类型a -> a,则无法使一个表达式的评估依赖于另一个表达式的评估。

于 2012-10-02T10:50:04.930 回答
11

在这个答案中给出了另一个反例- monads 不能满足 monad 法则sequndefined。并且由于undefined在图灵完备的语言中无法避免,因此应该归咎于seq.

于 2012-10-11T18:02:37.773 回答