Haskell/Classes and types

Back in Type basics II we had a brief encounter with type classes as the mechanism used with number types. As we hinted back then, however, classes have many other uses.

Broadly speaking, the point of type classes is to ensure that certain operations will be available for values of chosen types. For example, if we know a type belongs to (or, to use the jargon, instantiates) the class, then we are guaranteed, among other things, to be able to perform real division with its values.

Classes and instances
Up to now we have seen how existing type classes appear in signatures such as:

Now it is time to switch perspectives. First, we quote the definition of the  class from Prelude:

The definition states that if a type  is to be made an instance of the class   it must support the functions   and   - the class methods - both of them having type. Additionally, the class provides default definitions for  and   in terms of each other. As a consequence, there is no need for a type in  to provide both definitions - given one of them, the other will be generated automatically.

With a class defined, we proceed to make existing types instances of it. Here is an arbitrary example of an algebraic data type made into an instance of  by an instance declaration:

And now we can apply  and   to   values in the usual way:

A few important remarks:


 * The class  is defined in the Standard Prelude. This code sample defines the type   and then declares it to be an instance of  .  The three definitions (class, data type, and instance) are completely separate and there is no rule about how they are grouped. This works both ways: you could just as easily create a new class   and then declare the type Integer to be an instance of it.


 * Classes are not types, but categories of types, and so the instances of a class are types instead of values.


 * The definition of  for   relies on the fact that the values of its fields (namely   and  ) are also members of  .  In fact, almost all types in Haskell are members of Eq (the most notable exception being functions).


 * Type synonyms defined with type keyword cannot be made instances of a class.

Deriving
Since equality tests between values are commonplace, in all likelihood most of the data types you create in any real program should be members of Eq. A lot of them will also be members of other Prelude classes such as Ord and Show. To avoid large amounts of boilerplate for every new type, Haskell has a convenient way to declare the "obvious" instance definitions using the keyword deriving. So, Foo would be written as:

This makes <tt>Foo</tt> an instance of <tt>Eq</tt> with an automatically generated definition of <tt>==</tt> exactly equivalent to the one we just wrote, and also makes it an instance of <tt>Ord</tt> and <tt>Show</tt> for good measure.

You can only use <tt>deriving</tt> with a limited set of built-in classes, which are described very briefly below:


 * Eq : Equality operators <tt>==</tt> and <tt>/=</tt>


 * Ord : Comparison operators <tt>< <= > >=</tt>; <tt>min</tt>, <tt>max</tt>, and <tt>compare</tt>.


 * Enum : For enumerations only. Allows the use of list syntax such as <tt>[Blue .. Green]</tt>.


 * Bounded : Also for enumerations, but can also be used on types that have only one constructor. Provides <tt>minBound</tt> and <tt>maxBound</tt> as the lowest and highest values that the type can take.


 * Show : Defines the function <tt>show</tt>, which converts a value into a string, and other related functions.


 * Read : Defines the function <tt>read</tt>, which parses a string into a value of the type, and other related functions.

The precise rules for deriving the relevant functions are given in the language report. However, they can generally be relied upon to be the "right thing" for most cases. The types of elements inside the data type must also be instances of the class you are deriving.

This provision of special "magic" function synthesis for a limited set of predefined classes goes against the general Haskell philosophy that "built in things are not special", but it does save a lot of typing. Besides that, deriving instances stops us from writing them in the wrong way (an example: an instance of  such that   would not be equal to   would be flat out wrong).

Class inheritance
Classes can inherit from other classes. For example, here is the main part of the definition of <tt>Ord</tt> in Prelude:

The actual definition is rather longer and includes default implementations for most of the functions. The point here is that <tt>Ord</tt> inherits from <tt>Eq</tt>. This is indicated by the <tt>=></tt> notation in the first line, which mirrors the way classes appear in type signatures. Here, it means that for a type to be an instance of <tt>Ord</tt> it must also be an instance of <tt>Eq</tt>, and hence needs to implement the <tt>==</tt> and <tt>/=</tt> operations.

A class can inherit from several other classes: just put all of its superclasses in the parentheses before the <tt>=></tt>. Let us illustrate that with yet another Prelude quote:

Standard classes
This diagram, adapted from the Haskell Report, shows the relationships between the classes and types in the Standard Prelude. The names in bold are the classes, while the non-bold text stands for the types that are instances of each class (<tt>(->)</tt> refers to functions and <tt>[]</tt>, to lists). The arrows linking classes indicate the inheritance relationships, pointing to the inheriting class.



Type constraints
With all pieces in place, we can go full circle by returning to the very first example involving classes in this book:

is a type constraint, which restricts the type  to instances of the class. In fact,  is a method of , along with quite a few other functions (notably,   and  ; but not  ).

You can put several limits into a type signature like this:

Here, the arguments <tt>x</tt> and <tt>y</tt> must be of the same type, and that type must be an instance of both <tt>Num</tt> and <tt>Show</tt>. Furthermore, the final argument <tt>t</tt> must be of some (possibly different) type that is also an instance of <tt>Show</tt>. This example also displays clearly how constraints propagate from the functions used in a definition (in this case,  and  ) to the function being defined.

Other uses
Beyond simple type signatures, type constraints can be introduced in a number of other places:


 * declarations (typical with parametrized types);


 * declarations (constraints can be introduced in the method signatures in the usual way for any type variable other than the one defining the class );


 * declarations, where they act as constraints for the constructor signatures.

A concerted example
To provide a better view of the interplay between types, classes, and constraints, we will present a very simple and somewhat contrived example. We will define a  class, a   class which inherits from it, and a function with a   constraint implemented using the methods of the parent class, i.e..

A word of advice
Do not read too much into the  example just above; it is merely a demonstration of class-related language features. It would be a mistake to think that every single functionality which might be conceivably generalized, such as, needs a type class of its own. In particular, if all your  instances should be able to be moved as well then   is unnecessary - and if there is just one instance there is no need for type classes at all! Classes are best used when there are several types instantiating it (or if you expect others to write additional instances) and you do not want users to know or care about the differences between the types. An extreme example would be : general-purpose functionality implemented by an immense number of types, about which you do not need to know a thing before calling. In the following chapters, we will explore a number of important type classes in the libraries; they provide good examples of the sort of functionality which fits comfortably into a class.