我正在定义我自己的复数数据类型作为学习练习,并且我遇到了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
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])。
这里的主要痛苦来源是 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)。
如果我还没有说服你放弃这种疯狂,请随时查看有关高级重叠的页面。除了复杂和大量样板(并且需要打开大量语言扩展)之外,这个解决方案不是很通用(您仍然需要手动选择哪些类型适用于哪个实例)。