重写派生类型类实例的默认实现



我定义了一个数据类型,我允许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包[Hackage],这会为属于Generic类型类成员的类型进行实现:

{-# 语言派生通用#-} import Generics.Deriving.Base(Generic) import Generics.Deriving.Eq(GEq, geq) 数据整数或浮点数 = I 整数 |F 浮子 派生(通用) 实例 GEq IntegerOrFloat 实例 Eq IntegerOrFloat,其中 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基本函数。

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

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视为相同的值,这不是编译器对派生实例的假设。

最新更新