Typeclasses

Motivation: Equality testing with (==)

Suppose we define the following Color type, and want to implement an equality test for it.

A naive strategy would be to do the following:

Now, suppose we want to create an equality test for strings. We might define a function like this:

As you might’ve noticed, writing a separate function for every equality test is not great.

We really want a function

that compares two values of the same type. However, a single implementation of (==) could not capture the notion of equality for any possible type a, and would not permit us to extend (==) to operate on types that we create. Indeed, some types (such as functions) might not have a reasonable implementation of (==) yet the type above allows any type to be substituted for a.

A Typeclass to the Rescue

A typeclass allows us to give different definitions of a function for different types. This is similar to the notion of interfaces in Java. It can be seen as an extensible interface to some set of functions, and solves exactly the problem presented above. Consider the real type signature of (==),

The only difference between this and our hypothetical type signature above is Eq a =>. Things appearing before => in type signatures are typeclass constraints. In this case, the constraint says that a must belong to the typeclass Eq, meaning that a must be a type for which the function (==) is defined. A basic definition of the typeclass Eq could be

This tells us that for a type a to be an instance of Eq, we must define a function (==) with type a -> a -> Bool. Many primitive Haskell types are already instances of Eq, for example

Built-in Eq typeclass

Indeed, here is the definition of the built-in Eq typeclass:

Importantly, we only need to implement one of == or /=, since the compiler can figure out the other function by applying not.

Now, suppose we wanted to implement (==) for the Color type defined above. We just need to make Color an instance of the Eq typeclass. Indeed, we can write

Similarly, if we wanted to make Maybe a an instance of Eq, we could write:

Note that we have a typeclass constraint on a being in Eq, since otherwise we could not have x == y in the second line.

Automatic derivation

It turns out that GHC can automatically derive instances for certain typeclasses. Instead of manually writing instance Eq Color above, we can just write

On the type Color, this will automatically implement functions for string parsing (Read), string printing (Show), equality testing (Eq), and ordering (Ord).

References

Real World Haskell, Chapter 6.