Ellen Spertus
Ellen Spertus

Reputation: 6805

How does equality work for numeric types?

I see that Haskell allows different numeric types to be compared:

*Main> :t 3
3 :: Num t => t
*Main> :t 3.0
3.0 :: Fractional t => t
*Main> 3 == 3.0
True

Where is the source code for the Eq instance for numeric types? If I create a new type, such as ComplexNumber, can I extend == to work for it? (I might want complex numbers with no imaginary parts to be potentially equal to real numbers.)

Upvotes: 3

Views: 190

Answers (2)

clinux
clinux

Reputation: 3074

There is nothing magical going on here. 3 can be of either a real number or an integer, but its type gets unified with a real number when compared to 3.0.

Note the type class:

class Eq a where
  eq :: a -> a -> Bool

So eq really does only compare things of same type. And your example 3 == 3.0 gets its types unified and becomes 3.0 == 3.0 internally.

I'm unsure of any type tricks to make it unify with a user defined complex number type. My gut tells me it can not be done.

Upvotes: 2

leftaroundabout
leftaroundabout

Reputation: 120711

“Haskell allows different numeric types to be compared” no it doesn't. What Haskell actually allows is different types to be defined by the same literals. In particular, you can do

Prelude> let a = 3.7 :: Double
Prelude> let b = 1   :: Double
Prelude> a + b
4.7

OTOH, if I declared these explicitly with conflicting types, the addition would fail:

Prelude> let a = 3.7 :: Double
Prelude> let b = 1   :: Int
Prelude> a + b

<interactive>:31:5:
    Couldn't match expected type ‘Double’ with actual type ‘Int’
    In the second argument of ‘(+)’, namely ‘b’
    In the expression: a + b

Now, Double is not the most general possible type for either a or b. In fact all number literals are polymorphic, but before any operation (like equality comparison) happens, such a polymorphic type needs to be pinned down to a concrete monomorphic one. Like,

Prelude> (3.0 :: Double) == (3 :: Double)
True

Because ==, contrary to your premise, actually requires both sides to have the same type, you can omit the signature on either side without changing anything:

Prelude> 3.0 == (3 :: Double)
True

In fact, even without any type annotation, GHCi will still treat both sides as Double. This is because of type defaulting – in this particular case, Fractional is the strongest constraint on the shared number type, and for Fractional, the default type is Double. OTOH, if both sides had been integral literals, then GHCi would have chosen Integer. This can sometimes make a difference, for instance

Prelude> 10000000000000000 == 10000000000000001
False

but

Prelude> 10000000000000000 ==(10000000000000001 :: Double)
True

because in the latter case, the final 1 is lost in the floating-point error.

Upvotes: 10

Related Questions