I'm defining my own complex number data type as a learning exercise, and I've run into trouble overloading abs
along with the other members of Num
. As far as I know, only one instance definition is allowed per typeclass, but if I could I'd do something like this:
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
or
instance Floating a => Floating (Complex a) where
abs (Complex r i) = Complex (sqrt $ r^2 + i^2) 0
Because none of the members other than abs
require Floating
types, and I don't want to limit them to only Floating
types, but the abs
function is super important, and I don't want to unnecessarily exclude it.
Is there some way I can have the functions (+)
, (-)
, and (*)
work on all numeric types, while still implementing abs
?
According to 7.6.3.4. Overlapping instances in the GHC system guide, multiple instances can overlap if they differ on the type constraint(?) outside of the context (eg instance C [a]
and instance C [Int]
), with the compiler choosing the most specific instance for a given case, but it doesn't mention anything about only the context differing (eg instance C [a]
and instance Integral a => C [a]
).
There are five types of constraints: A NOT NULL constraint is a rule that prevents null values from being entered into one or more columns within a table. A unique constraint (also referred to as a unique key constraint) is a rule that forbids duplicate values in one or more columns within a table.
Multiple interface constraints can be specified. The constraining interface can also be generic.
The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.
Simply put this is constraining the generic parameter to a class (or more specifically a reference type which could be a class, interface, delegate, or array type).
The main source of pain here is that the Prelude's number hierarchy was defined to not be too complex - for most things, it works just fine. This is one of those edge cases where is doesn't really (although as @leftaroundabout points out, I'm not sure there are many applications for a Complex
over something that is not Floating
).
Your options are to
Floating a
constraint on Num (Complex a)
. This is what feels most natural to me and makes the most sense from a type class perspective - shoehorning in instance Num a => Num (Complex a)
breaks the Num
abstraction because it has no notion of abs
.Use a finer-grain numeric hierarchy. The numeric-prelude
comes to mind. In that, you will find the following (spread out over multiple modules):
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
In your case, you would be making instances instance Additive.C a => Additive.C (Complex a)
, instance Ring.C a => Ring.C (Complex a)
, and instance Algebraic.C a => Absolute.C (Complex a)
.
If I haven't yet managed to convince you to abandon this madness, feel free to check out this page on advanced overlap. Aside from being complex and boilerplate heavy (and needing to turn on a ton of language extensions), this solution isn't quite general (you'll still have to hand-pick which types go to which instance).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With