Introduction to Type Classes

Get introduced to the concept of type classes.

In the previous lesson, we defined our own data type for geometric shapes. In order to make them printable, we had to add deriving (Show) to our type declaration.

Show is an example of a Haskell type class. We also encountered the type class Eq, already, in type annotations of polymorphic functions (e.g. elem), where it acted as a constraint. In this lesson, we will learn in more detail what type classes are and how we can use them with our data types.

The Show type class

A type class is a collection of types that share a common property. For example, the type class Show is the class of all types that can be turned into a string using the show function (note the difference in capitalization). Its definition is:

class Show a where
  show :: a -> String

A type class declaration starts with the class keyword. What follows is the name of the class (Show) and a type variable a. After the where keyword, follow the operations that the members of the type class should support. This means that every type a, which belongs to the Show type class, must support the operation show. This turns the value of type a into a string. In that sense, type classes behave similarly to interfaces of object-oriented programming languages.

Most of Haskell’s predefined data types; such as numbers, characters, strings, tuples, and lists; are members of Show. Notable exceptions are function types that cannot be turned into strings.

Prelude> show 5
"5"

Prelude> show ([1],'c')
"([1],'c')"

Prelude> show (\x -> x + 1)
<interactive>:27:1: error:
    • No instance for (Show (Integer -> Integer))
        arising from a use of ‘show’
        (maybe you haven't applied a function to enough arguments?)
    • In the expression: show (\ x -> x + 1)
      In an equation for ‘it’: it = show (\ x -> x + 1)

Deriving type classes

As we saw in the previous lesson, type class instances can be automatically derived. All we need to do is add deriving (Show) at the end of our data declaration.

data Coordinates = Coordinates Double Double deriving (Show)

In that case, the compiler will automatically create a Show instance for a type with an inferred show implementation. Of course, this raises the question of what this implementation looks like.

In general, the derived show implementation prints types the same way that they are constructed. When we construct a Coordinates value like Coordinates 3.0 5.5, this means that the derived show implementation will print it as "Coordinates 3.0 5.5".

Implementing type classes

Sometimes, the default Show instance derived by the compiler will not be to our liking. For example, we might want to print a coordinates pair Coordinates 3.0 5.5 as "(3.0, 5.5)". In that case, we must refrain from deriving the Show instance and implement it ourselves. This can be done by using an instance declaration.

Get hands-on with 1200+ tech skills courses.