data CouldBe a = Is a | Lost deriving (Show, Ord)

instance Eq (CouldBe m) where
  Is x == Is y = x == y
  Lost == Lost = True
  _ == _ = False

给出错误:No instance for (Eq m) arising from a use of ‘==’所以:
instance (Eq m) => Eq (CouldBe m) where
  Is x == Is y = x == y
  Lost == Lost = True
  _ == _ = False

工作正常(至少我开始理解错误),但是为什么需要该约束?
我正在尝试学习,所以“为什么”对我来说非常重要。

最佳答案

您最初的定义是CouldBe mEq的任何类型m的实例,即使没有Eq实例的类型也是如此。但是,如果是这样,您就必须找到某种无需使用Is x == Is y即可定义x == y的方法(因为您不需要m具有Eq实例,因此不一定要定义x == y。)

作为一个具体示例,它可以防止您编写类似

Is (+3) == Is (* 5)  -- (+3) == (*5) is undefined

添加约束可确保仅在还可以比较包装类型的情况下才可以比较两个CouldBe值。

一个“有效”但微不足道的实例,没有添加约束:
instance Eq (CouldBe m) where
  Is x == Is y = True
  Lost == Lost = True
  _ == _ = False

只要两个CouldBe m值共享相同的数据构造函数,它们就相等,而与包装值无关。完全没有尝试使用xy,因此它们的类型可以不受限制。

“有效”用引号引起来,因为此定义可能违反在http://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Eq.html处定义的替代法则。假设您有一个函数可以分解CouldBe值:
couldbe :: b -> (a -> b) -> CouldBe a -> b
couldBe x _ Lost = x
couldBe _ f (Is x) = f x

发生冲突是因为Is 3 == Is 5为true,但是让f = couldbe 0 id出现。然后f (Is 3) == f (Is 5)评估为False的3 == 5

是否实际上是违反行为取决于couldbe之类的函数的存在,该函数可以在CouldBe值的内部“看到”。

08-19 23:56