Haskell/Advanced type classes

Type classes may seem innocuous, but research on the subject has resulted in several advancements and generalisations which make them a very powerful tool.

Multi-parameter type classes
Multi-parameter type classes are a generalisation of the single parameter type classes, and are supported by some Haskell implementations.

Suppose we wanted to create a 'Collection' type class that could be used with a variety of concrete data types, and supports two operations -- 'insert' for adding elements, and 'member' for testing membership. A first attempt might look like this:

This won't compile, however because the arguments to  must be of type   and   whereas we supply an. The problem is that the 'e' type variable in the Collection operations comes from nowhere -- there is nothing in the type of an instance of Collection that will tell us what the 'e' actually is, so we can never define implementations of these methods. Multi-parameter type classes solve this by allowing us to put 'e' into the type of the class. Here is an example that compiles and can be used:

Functional dependencies
A problem with the above example is that, in this case, we have extra information that the compiler doesn't know, which can lead to false ambiguities and over-generalised function signatures. In this case, we can see intuitively that the type of the collection will always determine the type of the element it contains - so if  is , then   will be. If  is , then   will be. (The reverse is not true: many different collection types can hold the same element type, so knowing the element type was e.g. , would not tell you the collection type).

In order to tell the compiler this information, we add a functional dependency, changing the class declaration to

A functional dependency is a constraint that we can place on type class parameters. Here, the extra  should be read '  uniquely identifies  ', meaning for a given , there will only be one. You can have more than one functional dependency in a class -- for example you could have  in the above case. And you can have more than two parameters in multi-parameter classes.

Matrices and vectors
Suppose you want to implement some code to perform simple linear algebra:

You want these to behave as much like numbers as possible. So you might start by overloading Haskell's Num class:

The problem comes when you want to start multiplying quantities. You really need a multiplication function which overloads to different types:

How do we specify a type class which allows all these possibilities?

We could try this:

That, however, isn't really what we want. As it stands, even a simple expression like this has an ambiguous type unless you supply an additional type declaration on the intermediate expression:

After all, nothing is stopping someone from coming along later and adding another instance:

The problem is that  shouldn't really be a free type variable. When you know the types of the things that you're multiplying, the result type should be determined from that information alone.

You can express this by specifying a functional dependency:

This tells Haskell that  is uniquely determined from   and.