为什么在Haskell中不绑定Enum的子类


9

似乎任何Bounded实例都应具有健全的Enum实现。我个人无法想到一个反例,尽管如果有人提出了一个非病态的例子,那么我会理解为什么不是这样。

从这:i两个类型类上看,似乎标准库中当前唯一的例外是元组,它们是有界的,但不是枚举。但是,任何绑定元组也必须以合理的方式可枚举,方法是简单地增加最后一个元素,然后在到达maxBound时环绕它。

此更改可能还涉及在Bounded中添加predB和/ nextB或类似的东西,以安全/循环的方式遍历Enum值。在这种情况下toEnum 0 :: (...)等于(toEnum 0, toEnum 0, ...) :: (...)


3
不能真正权威地回答此问题,但要考虑0到1之间的所有实数范围。它具有清晰的上下限,但具有无数个无限大的成员。
2013年

@Doval是一个公平的观点。但是,一般而言,所有实数(无穷无穷的成员)都可以说相同,但是Double/ Float和所有相似的类型Enum无论如何都实现,它们只是使succ = (+ 1)fromEnum = truncate。从实用性的角度来看,Haskell的方法实际上是有意义的,否则[0,0.5 ..]和类似的方法将不起作用,因此似乎Haskell不必担心枚举时的可数性。
分号

1
我不知道那succ是什么(+1)。这很奇怪,因为DoubleFloat没有无限的精度,因此可以枚举- succ可以将其定义为+1 ULP
2013年

2
@Doval我认为原因是因为Haskell核心团队希望[1 ..]在Doubles中具有与在Ints中相同的含义。
分号

@semicolon的double和float不是实数(例如,不能在不损失精度的情况下将PI存储在double中),因此它们是可枚举的
jk。

Answers:


8

我喜欢的一个实际示例来自编程语言领域:OO系统中的类型集是有界和离散的,但不可枚举,并且是部分有序但不是全部有序。

有问题的部分排序是子类型关系<:。然后,上限将是顶部类型(C#object和Scala调用Any),而下限将是底部类型(Scala的Nothing; C#/ Java没有等效的说法)。

但是,无法枚举类型系统中的所有类型,因此无法编写instance Enum Type。这应该很清楚:用户可以编写自己的类型,因此无法提前知道自己的类型。您可以枚举任何给定程序中的所有类型,但不能枚举整个系统中的所有类型。

同样,(根据子类型的某种合理定义)<:是自反的,传递的和反对称的,但不是总的。存在成对的类型对<:。(CatDog都是的子类型Animal,但都不是另一个的子类型。)


假设我们正在为一种简单的OO语言编写一个编译器。这是我们系统中类型的表示:

data Type = Bottom | Class { name :: String, parent :: Type } | Top

以及子类型关系的定义:

(<:) :: Type -> Type -> Bool
Bottom <: _ = True
Class _ _ <: Bottom = False
Class n t <: s@(Class m _)
    | n == m = True  -- you can't have different classes with the same name in this hypothetical language
    | otherwise = t <: s  -- try to find s in the parents of this class
Class _ _ <: Top = True
Top <: Top = True
Top <: _ = False

这也给我们带来了超类型化的关系。

(>:) :: Type -> Type -> Bool
t >: s = s <: t

您还可以找到两种类型的最小上限,

lub :: Type -> Type -> Type
lub Bottom s = s
lub t Bottom = t
lub t@(Class _ p) s@(Class _ q) =
    | t >: s = t
    | t <: s = s
    | p >: s = p
    | t <: q = q
    | otherwise = lub p q
lub Top _ = Top
lub _ Top = Top

练习:通过下面和下面两种方法,显示Type形成一个有边界的完整姿势<:>:


很好,谢谢!那完全回答了我的问题,也回答了关于Ord的后续问题。情商会有类似的问题吗?非相等类型可能具有maxBound或minBound的地方。在这种情况下,Cat == Dog应该返回false,因为它们是不同的类,还是由于树的位置不在另一个之上或之下而无法确定?
分号

排序意味着一个相等-只是定义x == y = x <= y && y <= x。如果我要设计一Poset门课,我会有class Eq a => Poset a。Google快速确认了其他人也有同样的想法
本杰明·霍奇森,2016年

抱歉,我的问题不明确。我的意思是,即使Bounded不暗示Ord,它是否也隐含Eq。
分号

@semicolon同样,这两个类之间没有任何关系。考虑使用和元素data Bound a = Min | Val a | Max增加类型。通过建设总是可以做出的一个实例,但只能将equatable如果基础类型是a+∞-∞Bound aBoundeda
本杰明·霍奇森

足够公平。我猜一个例子可能是带有和返回type值的函数Double,其中const (1/0)is maxBoundconst (negate 1/0)is minBound但but \x -> 1 - x\x -> x - 1无可比拟。
分号

4

这是因为操作是独立的,因此将它们与子类关系捆绑在一起实际上并不会给您带来任何好处。假设您要创建一个实现的自定义类型Bounded,也许将其Doubles限制在max和min之间,但是您不需要任何Enum操作。如果Bounded是子类,则Enum无论如何都必须实现所有功能,只是要对其进行编译。

是否有合理的实现Enum,或任何其他数量的类型类,这并不重要。如果您实际上并不需要它,则不应强迫您实现它。

将此与“说” Ord和“”进行对比Eq。在那里,Ord操作依赖于这些操作Eq,因此要求子类避免重复并确保一致性是很有意义的。


1
在这种情况下,它就是定义的一部分。从定义上讲,所有monad也是应用程序和函子,因此您必须先履行monad的“合同”,才能履行其义务。我对数学不太熟悉,无法确定这是基本关系还是强制性定义,但是无论哪种方式,我们现在都坚持下去。
Karl Bielefeldt

6
@semicolon的文档Bounded说:“ Ord不是Bounded的超类,因为未完全排序的类型也可能具有上限和下限。”
本杰明·霍奇森

1
@BenjaminHodgson甚至都没有考虑过部分排序的类型。+1用于非病理性示例和引用文档。
2013年

1
@semicolon来自计算机世界的部分排序示例可能是使用OO语言进行子类型化。<:为for 编写是的子类型∀ T S. T <: S ∨ S <: T不成立(例如int !<: bool ∧ bool !<: int)。如果您正在编写编译器,则可能会遇到这种情况。
本杰明·霍奇森

1
@BenjaminHodgson啊,好的。因此,例如,如果A是B和C的超类,而D是B和C的子类,那么B和C是不可比的,而A和D是max / min?
分号
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.