覆盖派生类型类实例的默认实现

Override default implementations for derived typeclass instances

我定义了一个数据类型,我允许 GHC 为其自动派生 Eq typeclass.

的实例

但是,派生实例并不具有我在所有情况下都需要的精确行为。

例如,查看此数据类型:

data IntegerOrFloat = I Integer | F Float
  deriving Eq

(I 0) == (F 0) 的计算结果为 False。我希望它评估为真。如果我正在编写实现,我可以简单地做:

instance Eq IntegerOrFloat where
  (I i) == (F f) = (fromIntegral i) == f

但是,我将不得不写其他三个案例。显然这对于​​这种类型来说是微不足道的,但这是一个人为的例子。我非常喜欢让大多数情况自动派生的便利。

有没有一种方法可以让我“覆盖”特定案例的派生实现,而不必手动编写整个实现?

您可以使用 generic-deriving package [Hackage],这会为属于 Generic 类型 class:

的类型实现
{-# LANGUAGE <b>DeriveGeneric</b> #-}

import Generics.Deriving.Base(Generic)
import Generics.Deriving.Eq(GEq, geq)

data IntegerOrFloat = I Integer | F Float
  deriving (<b>Generic</b>)

instance GEq IntegerOrFloat

instance Eq IntegerOrFloat where
    F f == I i = fromIntegral i == f
    I i == F f = fromIntegral i == f
    x == y = geq x y
因此,

geq :: GEq a => a -> a -> Bool 是一个自动生成 (==) 函数的实例,就像 Haskell 那样,然后我们可以使用 geq this 作为基函数。

您可以按原样保留派生类型,但仅导出覆盖了特殊情况的新类型包装版本:

data IntegerOrFloat' = I' Integer | F' Float
  deriving Eq

newtype IntegerOrFloat = IOF { getIOF :: IntegerOrFloat' }

instance Eq IntegerOrFloat where
  IOF (I i) == IOF (F f) = fromIntegral i == f
  IOF (F f) == IOF (I i) = f == fromIntegral i
  IOF x == IOF y = x==y

但我真的觉得这很可疑。如果您需要不同的 Eq 行为,那么您可能应该完全手动实现所有内容 – 不同的 Eq 实例意味着几乎所有内容都应该将 F 0I 0 视为相同的值,这不是编译器对派生实例的假设。