7
votes

When I am in a REPL like GHCI with Prelude, and I write

*> compare 5 7
LT

Why can I call that function (compare) like that directly in the REPL?

I know that compare is defined in typeclass Ord. The typeclass definition for Ord of course shows that it is a subclass of Eq.

Here is my line of reasoning: 5 has type Num a => a, and Num typeclass is not a subclass of Eq. Also,

Prelude> :t (compare 5)
(compare 5) :: (Num a, Ord a) => a -> Ordering

So, there is an additional constraint imposed here when I apply a numeric type argument. when I call compare 5 7, the types of the arguments are narrowed to something that does have an instance of Ord. I think the narrowing happens to the default concrete type associated with the typeclass: in the case of Num, this is Integer, which has an instance of Real, which has an instance of Ord.

However, coming from a non-functional programming background, I would have imagined that I would have to call compare on one of the numbers (like calling it on an object in OOP). If 5 is Integer, which does implement Ord, then why do I call compare in the REPL itself? This is obviously a question related to a paradigm shift for me and I still didn't get it. Hopefully someone can explain.

2

2 Answers

10
votes

The type defaulting here comes into play. The interpreter can derive that 5 and 7 need to be of the same type, and members of the Ord and Num typeclass. The default for a Num is Integer, and since Integer is an instance of Ord as well, we can thus use Integer.

The interpreter thus considers 5 and 7 to be Integers here in that case, and thus it can evaluate the function and obtain LT.

GHCi has some additional defaulting rules, described in the GHCi documentation.

9
votes

Methods like compare are associated with types, not particular values. The compiler needs to be able to deduce the type in order to select the correct typeclass instance, but that doesn't require any special assistance.

The type of compare is

compare :: (Ord a) => a -> a -> Ordering

Thus any of its arguments (of type a) can be used to look up the Ord instance.

As you correctly assumed, in the compare 5 7 example, the types of 5 and 7 default to Integer. Thus a in the compare type is deduced to be Integer and the Ord Integer instance is selected.

This selection does not necessarily go through a function argument. Consider e.g.

read :: (Read a) => String -> a

Here it is the result type that drives instance selection, but the type checker is just fine with it:

> read "(2, 3)" :: (Int, Int)
(2,3)

(What would the OO equivalent be? "(2, 3)".read()?)

In fact, methods don't even have to be functions:

maxBound :: (Bounded a) => a

This is a polymorphic value, not a function:

> maxBound :: Int
9223372036854775807

Class instances are uniquely connected to types, so as long as the type checker has enough information to figure out what that type variable represents, everything works out. That is, in

someMethod :: (SomeClass foo) => ...

foo has to appear somewhere in the type signature ... so the type checker can resolve SomeClass foo from the way someMethod is used at any given point (at least in the absence of certain language extensions).