【问题标题】:Override default implementations for derived typeclass instances覆盖派生类型类实例的默认实现
【发布时间】:2020-12-10 06:41:47
【问题描述】:

我已经定义了一个数据类型,我允许 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

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

有没有办法让我“覆盖”特定情况下的派生实现,而不必手动编写整个实现?

【问题讨论】:

    标签: haskell functional-programming ghc


    【解决方案1】:

    您可以使用generic-deriving package [Hackage],这可以实现属于Generic 类型类成员的类型:

    {-# LANGUAGE DeriveGeneric #-}
    
    import Generics.Deriving.Base(Generic)
    import Generics.Deriving.Eq(GEq, geq)
    
    data IntegerOrFloat = I Integer | F Float
      deriving (Generic)
    
    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 作为基础函数。

    【讨论】:

      【解决方案2】:

      您可以保留派生类型,但只导出一个新类型包装的版本,该版本覆盖了特殊情况:

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

      【讨论】:

      • 我真的想要一个deriving stock for newtype 机制。最常见的是,我想派生除length 之外的所有Foldable 方法。 generic-deriving 只做foldMap,这还不够。
      猜你喜欢
      • 2018-02-27
      • 2012-12-05
      • 1970-01-01
      • 1970-01-01
      • 2018-07-31
      • 1970-01-01
      • 2022-06-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多