同一实例的不同类型约束

Zoe*_*wll 5 haskell typeclass

我正在定义我自己的复数数据类型作为学习练习,并且我遇到了absNum. 据我所知,每个类型类只允许一个实例定义,但如果可以的话,我会做这样的事情:

instance Num a => Num (Complex a) where
    (+) (Complex ra ia) (Complex rb ib) = Complex (ra + rb) (ia + ib)
    (-) (Complex ra ia) (Complex rb ib) = Complex (ra - rb) (ia - ib)
    (*) (Complex ra ia) (Complex rb ib) = Complex (ra*rb - ia*ib) (ra*ib + rb*ia)
    fromInteger r = Complex (fromInteger r) 0

instance Floating a => Num (Complex a) where
    abs (Complex r i) = Complex (sqrt $ r^2 + i^2) 0
Run Code Online (Sandbox Code Playgroud)

或者

instance Floating a => Floating (Complex a) where
    abs (Complex r i) = Complex (sqrt $ r^2 + i^2) 0
Run Code Online (Sandbox Code Playgroud)

因为除了abs需要Floating类型之外,其他成员都没有,我不想将它们限制为仅Floating类型,但是abs功能非常重要,我不想不必要地排除它。
有一些方法可以让我具备的功能(+)(-)以及(*)工作在所有数字类型,同时还实现abs

根据7.6.3.4。GHC 系统指南中的重叠实例,如果多个实例在上下文(例如instance C [a]instance C [Int])之外的类型约束(?)上不同,则它们可以重叠,编译器为给定的情况选择最具体的实例,但它没有提到仅与上下文不同的任何内容(例如instance C [a]instance Integral a => C [a])。

Ale*_*lec 5

这里的主要痛苦来源是 Prelude 的数字层次结构被定义为不太复杂 - 对于大多数事情,它工作得很好。这是那些边缘情况下,是没有真正的(尽管@leftaroundabout指出,我不知道还有一个许多应用一个Complex以上的东西是不是 Floating)。

你的选择是

  • 在 上添加Floating a约束Num (Complex a)。这对我来说是最自然的,并且从类型类的角度来看也是最有意义的——硬闯入instance Num a => Num (Complex a)打破了Num抽象,因为它没有abs.
  • 使用更细粒度的数字层次结构。该numeric-prelude想到的。在其中,您会发现以下内容(分布在多个模块中):

    class (Field.C a) => Algebraic.C a where
      sqrt :: a -> a
    
    class (Ring.C a) => Field.C a where
      (/)           :: a -> a -> a
      recip         :: a -> a
      fromRational' :: Rational -> a
      (^-)          :: a -> Integer -> a
    
    class (Ring.C a) => Absolute.C a where
      abs    :: a -> a
      signum :: a -> a
    
    class (Additive.C a) => Ring.C a where
      (*)         :: a -> a -> a
      one         :: a
      fromInteger :: Integer -> a 
      (^)         :: a -> Integer -> a
    
    class Additive.C a where
      zero     :: a
      (+), (-) :: a -> a -> a
      negate   :: a -> a
    
    Run Code Online (Sandbox Code Playgroud)

    在你的情况,你会做实例instance Additive.C a => Additive.C (Complex a)instance Ring.C a => Ring.C (Complex a)instance Algebraic.C a => Absolute.C (Complex a)

  • 如果我还没有说服你放弃这种疯狂,请随时查看有关高级重叠的页面。除了复杂和大量样板(并且需要打开大量语言扩展)之外,这个解决方案不是很通用(您仍然需要手动选择哪些类型适用于哪个实例)。