具有和不具有类型类约束的 Eq 实例

Instance of Eq with and without Typeclass Constraints

这是我的代数数据类型 (ADT) 的 Eq 实现

data Stateful a =
  Advancing a
  | Stable a
  | Finished a

instance statefulEq :: (Eq a) => Eq (Stateful a)
  where
    eq (Advancing x) (Advancing y) = eq x y
    eq (Stable x) (Stable y) = eq x y
    eq (Finished x) (Finished y) = eq x y
    eq _ _ = false

这说明(我希望)如果 Stateful 的元素有一个 Eq 的实例,那么 Stateful 有一个 Eq 的实例

有什么方法可以更改它以允许 second/backup 实施吗?在SudoCode伪代码中:

instance statefulEq :: Eq (Stateful a)
  where
    eq (Advancing (Eq x)) (Advancing (Eq y)) = eq x y
    eq (Advancing x) (Advancing y) = true
...

一种说法:如果元素有 Eq 的实例,则使用它,否则 return true。

或者如果我有一些功能 isConstrained :: forall a. TypeClass -> a -> Boolean

然后也许

instance statefulEq :: Eq (Stateful a)
  where
    eq (Advancing x) (Advancing y)
      | isConstrained Eq x && isConstrained Eq x = eq x y
      | otherwise = true
...

不,你不能这样做,这是有充分理由的。

你看,约束并不是普遍存在的,它们必须在“范围内”,这基本上意味着从定义它们的模块中导入。

这意味着您的类型的 Eq 约束可以在范围内,也可以不在范围内,您的程序行为将取决于约束是否在范围内。通俗地说,添加一个看似无关的模块导入可能会在没有任何警告的情况下改变你的程序行为。

这种“远距离操作”在实践中是一个足够大的错误来源,Haskell 和 PureScript 明确禁止它。


另外,附注:Eq (Stateful a) 的实现与自动派生的实现 100% 等效。您可以将整个内容替换为:

derive instance statefulEq :: Eq a => Eq (Stateful a)