同一实例的不同类型约束

时间:2017-02-06 12:16:55

标签: 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

instance Floating a => Floating (Complex a) where
    abs (Complex r i) = Complex (sqrt $ r^2 + i^2) 0

因为abs以外的所有成员都不需要Floating类型,我不想将它们仅限于Floating类型,但abs函数是非常重要,我不想不必要地排除它 我是否可以通过某种方式将函数(+)(-)(*)用于所有数字类型,同时仍然实现abs

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

1 个答案:

答案 0 :(得分:4)

痛苦的主要来源是Prelude的数字层次被定义为不太复杂 - 对于大多数事情来说,它的工作正常。这是其中一个没有真正的边缘情况(虽然正如@leftaroundabout指出的那样,我不确定Complex对于不是 { {1}})。

您的选择是

  • Floating上添加Floating a约束。这对我来说是最自然的,并且从类型的角度来看最有意义 - Num (Complex a)中的shoehorning打破了instance Num a => Num (Complex a)抽象,因为它没有Num的概念。
  • 使用更精细的数字层次结构。我想起了numeric-prelude。在那里,您将找到以下内容(分布在多个模块上):

    abs

    在您的情况下,您将制作实例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 instance Additive.C a => Additive.C (Complex a)instance Ring.C a => Ring.C (Complex a)

  • 如果我还没有设法说服你放弃这种疯狂,请随时查看this page on advanced overlap。除了复杂和样板重(并且需要开启大量的语言扩展)之外,这个解决方案还不是很普遍(你仍然需要亲自选择哪种类型去哪个实例)。

    < / LI>