Skip to content

Defining Type Classes and Instances

So how is this mysterious type class Eq defined? Here is its definition:

class Eq a where
    (==) :: a -> a -> Bool
    (/=) :: a -> a -> Bool

Every class definition starts with the keyword class, followed by the name of the class (here, Eq), followed by a type variable used to refer to the type that is an instance of this class (here, a).1 This is followed by where and a bunch of function signatures that refer to the type a. An instance of the type class must provide an implementation for each of the function signatures listed in the class definition. We'll discuss this shortly.

The definition of the Eq type class says that any type a that is an instance of Eq must provide implementations of the two functions (==) and (/=), of type a -> a -> Bool. These two functions implement equality and inequality testing, respectively.

Standard types such as Int, Integer, Bool, etc. are all instances of this type class, but type classes would have fairly limited use if we couldn't make the types we define ourselves instances of different type classes.

Let's look at our banner number type:

newtype BannerNumber = B Int

Out of the box, this type is not an instance of any type class. We can't even test BannerNumbers for equality. That's a good thing, because it allows us to control exactly what operations our types should support. Remember, when I introduced the BannerNumber type in the chapter on data types, I said that we want this type to be distinct from Int because, for example, adding two banner numbers together is generally nonsense. And, indeed, as we discuss below, the types that support arithmetic operations are those in the Num type class. Unless we make BannerNumber an instance of Num, we're safe: the compiler complains if we try to add two banner numbers:

GHCi
>>> newtype BannerNumber = B Int
>>> B 1 + B 2

<interactive>:82:5: error:
    • No instance for (Num BannerNumber) arising from a use of ‘+’
    • In the expression: B 1 + B 2
      In an equation for ‘it’: it = B 1 + B 2

The error message is similar to the one we encountered for our initial implementation of elem. Given that we're trying to add two banner numbers together, the compiler concludes that banner numbers should support the addition operation (+). This is one of the functions provided by the Num type class, so the compiler further concludes that to add two banner numbers together, BannerNumber must be an instance of Num. Since we defined no such instance, we get an error.

Similarly, we currently don't have equality tests for banner numbers, so testing whether a list of banner numbers contains a specific number also doesn't work:

GHCi
>>> newtype BannerNumber = B Int
>>> B 1 `elem` [B 1, B 2, B 3]

<interactive>:2:5: error:
    • No instance for (Eq BannerNumber) arising from a use of ‘elem’
    • In the expression: B 1 `elem` [B 1, B 2, B 3]
      In an equation for ‘it’: it = B 1 `elem` [B 1, B 2, B 3]

We probably do want to be able to test whether two banner numbers are equal. Searching a list of banner numbers for a specific one is one example where this would be useful. To do so, we need to make BannerNumber an instance of Eq. Clearly, we want two banner numbers to be the same if the Ints they wrap are the same. Thus, we declare BannerNumber an instance of Eq as follows:

instance Eq BannerNumber where
    B x == B y = x == y
    B x /= B y = x /= y

This says that BannerNumber is an instance of Eq and provides the two required implementations of (==) and (/=) for banner numbers. Here, two banner number B x and B y are equal (B x == B y) if and only if x == y, and they are unequal (B x /= B y) if and only if x /= y. Pretty straightforward.

Now we can test banner numbers for equality:

GHCi
>>> :{
  | instance Eq BannerNumber where
  |     B x == B y = x == y
  |     B x /= B y = x /= y
  | :}
>>> B 1 == B 2
False
>>> B 1 /= B 2
True
>>> B 3 == B 3
True
>>> B 3 /= B 3
False

Searching a list for a specific banner number also works now:

GHCi
>>> B 1 `elem` [B 1, B 2, B 3]
True
>>> B 4 `elem` [B 1, B 2, B 3]
False

What about asking whether a banner number is less than another?

GHCi
>>> B 1 < B 2

<interactive>:98:5: error:
    • No instance for (Ord BannerNumber) arising from a use of ‘<’
    • In the expression: B 1 < B 2
      In an equation for ‘it’: it = B 1 < B 2

Nope, this doesn't work. And this shouldn't be surprising. We only provided implementations of (==) and (/=). That's the only thing guaranteed by the Eq type class. We didn't provide implementations of (<), (>), (<=) or (>=). Those are part of the Ord type class we discuss shortly. That's exactly what GHCi complains about here: "No instance for (Ord BannerNumber) arising from a use of '<'."

You may question why one would want to support equality tests but not less-than and greater-than tests. Here's an example. Consider two binary trees. It makes perfect sense to ask whether two binary trees are the same, and there exists a natural recursive implementation, which we will discuss shortly. In general, there is no meaningful way to define that one binary tree is less than another binary tree. So binary trees should be instances of Eq but not of Ord. Similarly, there are types where it doesn't even make sense to support equality testing. Functions are one example. Well, mathematically, it actually makes perfect sense to ask whether two functions are the same. The reason why we can't provide an Eq instance for function types is that the only way to test whether two functions are the same is to verify whether they have the same type—that's easy—and they produce the same result for every possible argument value—that's the part that takes way too long in general or can't even be done at all for functions unbounded argument types, such as Integer.


  1. There also exist some type classes with multiple type variable arguments. For a type class with two arguments, for example, the instances are no longer individual types but pairs of types. Where a one-argument type class, such as Eq, specifies some properties of all types that are instances of this type class, you should think about a multi-parameter type class as establishing a relationship between pairs or tuples of types. We'll encounter these multi-parameter type classes later in this book.