为什么Haskell在Real上实现Enum?
该Enum类型类意味着实现类型可以在一些有意义的方式订购。在 Haskell 中,Real类型实现Enum. 来自数学背景,这很奇怪。一百年前,Georg Cantor 证明了实数不能被索引,也就是说,没有办法说出所有实数的第 n 个实数是多少。
现在,诸如此类的具体类型Double实际上具有有限域。所以你可以争辩说他们可以实现Enum. 人们会假设 a 的后继者Double将是下一个有效的Double。相反,它只是添加了1。因此,我们看到了如下奇怪的现象:
Prelude> succ (1e20 :: Double) == (1e20 :: Double)
True
在我看来,这种行为破坏了Enum. 任何人都可以解释这背后的原因吗?
编辑:更正Ord到Enum并澄清实数不为零艾礼富。
Post-Script:这条评论照亮了我的答案:
Enum Double 现在被几个 Haskeller 认为是一个错误。添加它是为了允许例如 [1.0 .. 20.0] 以一个单位的步长进行计数。这要求 succ 是 (+1) 而不是真正的“下一个”双精度值。此外,它会打开一堆蠕虫,因为长度 [1.0 .. 99.5] 可能是 99 或 100,具体取决于舍入误差。更糟糕的是,这无法修复,它本质上是脆弱的。
回答
正如 Robin Zigmond 评论的那样,您混淆了Ord和Enum类。Ord是什么是一个超类Real,但Enum就是succ属于。
Ord不允许您枚举或以其他方式生成任何类型的元素,它只允许您比较给定的元素。并且能够检查是否x < y为两个实数看起来非常简单且没有争议。†
相比之下,这Enum门课在数学上绝对是一团糟。这不是用于枚举类型(即Universe)的所有值的类,而是您应该将其视为可用于表单[1, 1.5, .. 9]或 的列表构建器的类['q'..]。这些实际上没有任何明确的数学解释,它们只是用于编写简洁实用的代码。
有人认为,Float和Double不应该有Enum实例。但是 IMO 就类本身而言,这些实例是可以的 - 不好,但还不足以保证用新的东西替换它的麻烦(并阻止大量使用列表推导式的现有代码)。
†实际上,这值得考虑。不像x < y,这通常是一个非常好的检查,x==y实际上在某些方面是有问题的,无论是数学上的精确实数还是实际的浮点数。从建设性的数学角度来说,您所能做的就是在有限时间内检查x < y或x > y,但您永远无法确定两个值是否相等。对于浮点数,您永远不应该假设两个值==相等,即使它们来自数学上等效的比较。相反,您在测试中应该做的是检查x-? < y < x+?一些小的 ?。(多小合适可能很难确定。)
- “鉴于`x<y`,可以确认这一点。” 好的,但这并不意味着 `x<y` 有一个决策过程;决策程序必须始终给出答案。所以检查 `x<y` 并不是很好。或者,换一种说法:*给定*`x<y`,可以检查`x==y`;那么为什么要区分 `==` 和 `<`?
- 是否有一种可计算的算法可以始终成功确定一个实数是否小于另一个实数?我认为这是不可能的,因为在某些情况下,两个可计算的数字可能*真正* 彼此接近,或者它们可能完全相等并且无法分辨哪个,使其成为半可判定的。IIRC 这与为什么可计算实数上的每个可计算函数都是连续的有关,基于 [例如本文的第 2 节和第 4 节(如果我理解正确的话)](http://math.andrej.com/wp-content/上传/2014/03/real-world-realizability.pdf)