覆盖特定模式的默认Eq定义

时间:2017-05-31 21:20:38

标签: haskell

假设我定义了一些派生Eq但希望为某些模式插入我自己的(==)定义的数据类型。有没有办法做到这一点,还是我必须为每个模式定义(==)?

e.g。

data Asdf = One Char | Two Char Char --(deriving Eq)
instance Eq Asdf where
  (==) (One _) (One _) = True
  --otherwise use what the derived definition would have done
  --can I do this without defining these patterns myself?

2 个答案:

答案 0 :(得分:8)

要做你想做的事情,你必须自己定义,这意味着你必须为每个模式定义它。

基本上data MyType x = A x | B x x deriving (Eq)将添加等效于

的默认派生
instance Eq x => Eq (MyType x) where
    A x1    == A x2     = x1 == x2
    B x1 x2 == B x3 x4  = x1 == x3 && x2 == x4
    _       == _        = False

请注意,它确定了必要的依赖关系(上面的Eq x =>部分)以及填充对角线情况 - n 2 中的特殊情况使用相同构造函数的可能匹配。

据我所知,它一次性完成这个定义,并且无法深入挖掘现有的实例声明来解决它 - 并且有充分的理由这样做;如果他们让你这样做,那就意味着随着代码库的增长,你无法查看实例派生或deriving (Eq)子句,并确信你确切知道它的含义,因为代码的其他部分可能猴子补丁,Eq实例做恶事。

所以一种方法是自己重新定义对角线。但这不是唯一的方式。如果修改多个使用网站比将所有 n 构造函数推送到一个单独的东西中更容易,那么至少有一个替代方案可以工作:

newtype EverythingIsEqual x = E x deriving (Show)
instance Eq (EverythingIsEqual x) where
    _ == _ = True

data MyType x = A (EverythingIsEqual x) | B x x deriving (Show, Eq, Ord)

这个newtype允许您策略性地修改某些术语,以便在没有运行时成本的情况下拥有不同的Eq关系 - 实际上这几乎是newtypes的两个核心参数之一;除了较小的一个“我希望这两个字符串之间存在类型级差异,但它们只是字符串而且我不想支付任何性能损失”,还有更大的争论“有时我们想告诉Haskell使用不同的Ord字典而不会弄乱这个字典所依据的任何值,我们只想换掉这些函数。“

答案 1 :(得分:5)

此问题讨论了如何使用https://hackage.haskell.org/package/generic-deriving包对Show实例执行非常类似的操作:Accessing the "default show" in Haskell?

特别参见这个答案:https://stackoverflow.com/a/35385768/936310

我最近最近将它用于Show实例,并且效果非常好。您可以类似地为您的类型派生Eq,假设它足够常规。