In this article, I'm exploring classes in Haskell. Coming from object oriented languages, the concept of Haskell classes was a bit confusing for me. Haskell is a functional programming language, so its class functionality doesn't match classes in object oriented languages such as Java or Python. The closest comparison for Haskell classes in the object oriented world is Java interfaces with default methods1. This article helps clear the confusion of Haskell classes.
My previous Haskell post looked at declaring custom types. Custom types and existing types can become instances of classes, giving them functionality. Type classes are created with a class declaration and instances are created with an instance declaration.
As I mentioned earlier, a class is similar to a Java interface. Therefore it provides operator and function declarations without bodies. For example, the following Eq class matches the one in the Prelude module that checks for equality.
The class Eq defines two operators, == and /=. It also creates a default definition for the /= operator (which is simply the opposite of ==). If an instance of Eq does not implement the /= operator, its functionality falls back to the default definition2.
A type is defined as an instance of a class using an instance declaration. The following code declares Int as an instance of Eq.
While I didn't have to implement (/=) due to the default definition, there is no harm in implementing it anyway. eqInt and neInt are internal Haskell functions used to determine integer equality.
Thanks to the Eq class and corresponding instance declaration, any two Int types can be used with the == and /= operators.
Just like built-in types, instances can be created with custom types. The following code creates a custom FirTree type and declares it as an instance of Eq.
I tested the == and /= operators with FirTree types.
Type classes can extend one of many existing classes. Class extensions are a form of class inheritance, and a class that extends multiple classes demonstrates multiple inheritance. I do feel nervous speaking of Haskell classes in object oriented terms simply because there are many differences between the two. These differences become apparent when dealing with multiple inheritance and class extensions.
A simple example of a class extension from the Prelude module is the Ord class which extends Eq.
One of the major misconceptions I had about class extensions was a result of my object oriented background. I figured that instances of Ord would inherit the operators and functions from Eq, but that is not the case. What a class extension actually means is that for a type to be an instance of Ord it must also be an instance of Eq3. This is much different than object oriented class inheritance.
To make the FrasierFir, BalsamFir, and DouglasFir types instances of FirTree, they first must be instances of Tree, ChristmasTree, and EvergreenTree.
Next I tested the functionality provided by the type classes.
As this example demonstrates, Haskell provides multiple inheritance of classes. However, Haskell classes are more like interfaces, and instances of subclasses must explicitly be instances of all parent classes in the inheritance hierarchy.