Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I call a function from a typeclass instance directly in the REPL (like compare from Ord)?

Tags:

haskell

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.

like image 458
evianpring Avatar asked Aug 11 '19 21:08

evianpring


People also ask

What is a Typeclass in Haskell?

What's a typeclass in Haskell? A typeclass defines a set of methods that is shared across multiple types. For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.

What is-> in haskell?

(->) is often called the "function arrow" or "function type constructor", and while it does have some special syntax, there's not that much special about it. It's essentially an infix type operator. Give it two types, and it gives you the type of functions between those types.

What is EQ a Haskell?

The Eq typeclass provides an interface for testing for equality. Any type where it makes sense to test for equality between two values of that type should be a member of the Eq class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.

What is a type class dictionary?

Type classes define interfaces that in Haskell's terms are called dictionaries. For instance, from the Ord class definition the compiler will create a dictionary that stores all the class methods.


2 Answers

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.

like image 186
Willem Van Onsem Avatar answered Oct 17 '22 01:10

Willem Van Onsem


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).

like image 40
melpomene Avatar answered Oct 17 '22 00:10

melpomene