据我所知,多态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
/ build
fusion 在seq
. 如果您对存在 seq 的自由定理的更多细节感兴趣,seq
请查看存在 seq 的自由定理.
据我所知,当多态seq
被添加到语言中时,它会破坏某些转换。然而,替代品也有缺点。如果您添加一个基于类型类的seq
,您可能必须在程序中添加大量类型类约束,如果您在seq
某个深处添加了一个。此外,省略也不是一个选择,seq
因为已经知道存在可以使用seq
.
最后,我可能会错过一些东西,但我看不出seq
类型运算符是如何a -> a
工作的。的线索seq
是,如果另一个表达式被评估为头范式,它会将一个表达式评估为头范式。如果seq
具有类型a -> a
,则无法使一个表达式的评估依赖于另一个表达式的评估。