我将自己的复数数据类型定义为学习练习,我遇到了abs
和Num
的其他成员重载问题。据我所知,每个类型类只允许一个实例定义,但如果可以,我会做这样的事情:
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]
)。
答案 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>