user2108462
user2108462

Reputation: 865

Why can't you compare Ints to Nums or Ords?

I have a function:

comp :: (Ord a) => a -> a -> Bool
comp a b = if a > b then True else False

This works. But if I do this:

comp :: (Num a) => Int -> a -> Bool
comp a b = if a > b then True else False

or this:

comp :: (Ord a) => Int -> a -> Bool
comp a b = if a > b then True else False

I get an error message:

Could not deduce (a ~ Int)

Huh? Doesn't Num and Ord include Int? Aren't Num and Ord only supposed to include reals and not complex numbers?

Why is it that in the interpreter I can compare any real to any int:

4 > 4.5 
False

But I'm not allowed to define a function that compares Ints to Nums or Ords?

Upvotes: 3

Views: 1022

Answers (4)

Benjamin Hodgson
Benjamin Hodgson

Reputation: 44634

> expects two Ord instances of the same type.

ghci> :t (>)
(>) :: Ord a => a -> a -> Bool

That type signature is not the same as (Ord a, Ord b) => a -> b -> Bool, which would be the signature of a function which can compare any two instances of Ord. (A non-trivial version of such a function does not exist.)

The reason > places such a draconian requirement on the poor programmer is that different types may have different ordering semantics. How would you compare an Int to a [Bool]?* Even though they are both instances of Ord, it doesn't make sense to compare them and the compiler won't allow it:

ghci> 3 > [True, True, False]

<interactive>:6:1:
    No instance for (Num [Bool]) arising from the literal `3'
    Possible fix: add an instance declaration for (Num [Bool])
    In the first argument of `(>)', namely `3'
    In the expression: 3 > [True, True, False]
    In an equation for `it': it = 3 > [True, True, False]

Each instance of Ord defines its own version of >. That's why you can't compare an Int to a Float:

ghci> (3 :: Int) > (4 :: Float)

<interactive>:4:15:
    Couldn't match expected type `Int' with actual type `Float'
    In the second argument of `(>)', namely `(4 :: Float)'
    In the expression: (3 :: Int) > (4 :: Float)
    In an equation for `it': it = (3 :: Int) > (4 :: Float)

* To be pedantic, you could devise some way of representing an integer in binary as a list of booleans. Such a representation would be a good candidate for a newtype with its own instances of Num and Ord.


The reason 4 > 4.5 works is that the literal 4 is polymorphic. It can stand in for the '4' value of any numeric type:

ghci> :t 4
4 :: Num a => a

Similarly, 4.5 can take on any fractional type (4.5 :: Fractional a => a). The compiler is smart enough to realise that you are comparing a value of type "any number" to a value of type "any fractional number" and uses the most convenient concrete type available, which in this case is Double. (Double is declared as a default in the standard prelude.) So in the expression 4 > 4.5, 4 and 4.5 will both be Doubles.

You can force the comparison to use the Ord instance of a particular concrete type by supplying a type signature to one of the operands.

Upvotes: 11

John F. Miller
John F. Miller

Reputation: 27217

Ok, but let's say you did want to write the function

comp :: (Num a, Ord a) => Int -> a -> Bool

The function you need is fromIntegral :: (Integral a, Num b) => a -> b. One of the properties of a Num type in Haskell is that it must implement a sane version of this function. The resulting implementation would be

comp n x = fromIntegral n < x

Upvotes: 3

hammar
hammar

Reputation: 139840

All the comparison operators in Ord have the type Ord a => a -> a -> Bool, so you can only ever compare two values of the same type.

Why is it that in the interpreter I can compare any real to any int:

4 > 4.5 
False

You're not. You are comparing two Doubles. Integer literals are overloaded in Haskell, so 4 can have any Num type depending on the context.

> :t 4
4 :: Num a => a

The same is true for decimal literals, which can be any Fractional type.

> :t 4.5
4.5 :: Fractional a => a

Based on these constraints, and the fact that the two sides should have the same type, it defaults both sides to Double.

> :set -fwarn-type-defaults
> 4 > 4.5

<interactive>:6:5: Warning:
    Defaulting the following constraint(s) to type `Double'
      (Fractional a0)
        arising from the literal `4.5' at <interactive>:6:5-7
      (Num a0) arising from the literal `4' at <interactive>:6:1
      (Ord a0) arising from a use of `>' at <interactive>:6:3
    In the second argument of `(>)', namely `4.5'
    In the expression: 4 > 4.5
    In an equation for `it': it = 4 > 4.5

<interactive>:6:5: Warning:
    Defaulting the following constraint(s) to type `Double'
      (Fractional a0)
        arising from the literal `4.5' at <interactive>:6:5-7
      (Num a0) arising from the literal `4' at <interactive>:6:1
      (Ord a0) arising from a use of `>' at <interactive>:6:3
    In the second argument of `(>)', namely `4.5'
    In the expression: 4 > 4.5
    In an equation for `it': it = 4 > 4.5

Upvotes: 4

chi
chi

Reputation: 116139

The type

comp :: (Num a) => Int -> a -> Bool

states that comp can work on an Int and any numeric type, i.e., a numeric type chosen by who calls this function. Since the caller can choose a complex type, it is impossible to implement comp in a meaningful way.

The type

comp :: (Ord a) => Int -> a -> Bool

states that comp can work on an Int and any ordered type, i.e., an ordered type chosen by who calls this function. Since the caller can choose String, or (Int,Int), it is impossible to implement comp in a meaningful way.

Upvotes: 3

Related Questions