Haskell有一个名为seq
的神奇函数,它接受任何类型的参数并将其缩小为弱头范式(WHNF)。
我已经阅读了几个来源[并不是我能记得他们现在 ...],他们声称“多态seq
是坏的”。他们以什么方式“坏”?
同样,还有rnf
函数,可以减少 Normal Form (NF)的参数。但是这个是一种类方法;它不适用于任意类型。对我来说似乎“显而易见”可以改变语言规范,将其作为内置基元提供,类似于seq
。据推测,这可能比仅仅seq
“更糟糕”。这是怎么回事?
最后,有人建议给seq
,rnf
,par
和类似id
函数的类型,而不是const
函数现在,这将是一个进步。怎么样?
答案 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无法满足seq
和undefined
的monad法则。由于使用图灵完备语言无法避免使用undefined
,因此责怪的是seq
。