Introduction to Haskell Typeclasses
Tags: haskell
Published: 2022-04-26 16:30:02

---
title: Introduction to Haskell Typeclasses
published: true
date: 2022-04-26 00:00:00 UTC
tags: haskell
canonical_url: https://serokell.io/blog/haskell-typeclasses
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3oavmun5tiv6cdueuhjc.png
---

Imagine you’ve been tasked to write a function for increasing a value by one in Haskell.

What’s easier? Finally, a place where one can use previous JavaScript experience. 😅

You find the type of bounded integers – `Int` – and get to work.

```
plusOne :: Int -> Int
plusOne a = a + 1

```

But it turns out that the team lead also wants a plus function that works with floating-point numbers.

```
plusOneFloat :: Float -> Float
plusOneFloat a = a + 1

```

Both of these requests could definitely be covered by a more generic function.

```
plusOnePoly :: a -> a
plusOnePoly a = a + 1

```

Unfortunately, the code above doesn’t compile.

```
No instance for (Num a) arising from a use of '+'

```

To sum two members of the same type in Haskell via `+`, their type needs to have an instance of the `Num` typeclass.

But what’s a typeclass, and what’s an instance of a typeclass? Read further to find the answers.

I’ll cover:

- what typeclasses are;
- how to use them;
- how to create instances of typeclasses;
- basic typeclasses like `Eq`, `Ord`, `Num`, `Show`, and `Read`.

Recommended previous knowledge: [algebraic data types](https://serokell.io/blog/algebraic-data-types-in-haskell).

## What’s a typeclass in Haskell?

A typeclass defines a set of methods that is shared across multiple types.

For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.

As an example, let’s look at the `Num` typeclass in Haskell.

```
class Num a where
  (+) :: a -> a -> a
  (-) :: a -> a -> a
  (*) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a

```

For a type to belong to the `Num` typeclass, it needs to implement its methods: `+`, `-`, `*`, and so forth.

If you want to use one of its methods, such as `+`, you can only use it on types that have an instance of `Num`.

And a function that uses `+` needs to limit itself by only taking members of the `Num` typeclass. Otherwise, it won’t compile.

This is done by putting a type constraint (`Num a =>`) in the type signature.

```
plusOnePoly :: Num a => a -> a
plusOnePoly a = a + 1

```

This stands in contrast to polymorphism across all types. For example, `++` will work with two lists of elements of the same type, no matter what that type is.

```
Prelude> :t (++)
(++) :: [a] -> [a] -> [a]

```

Typeclasses are similar to Java interfaces, Rust traits, and Elixir protocols, but there are also [noticeable differences](https://diogocastro.com/blog/2018/06/17/typeclasses-in-perspective/).

## What’s an instance of a typeclass?

A type has an instance of a typeclass if it implements the methods of that typeclass.

We can define these instances by hand, but Haskell can also do a lot of work for us by deriving implementations on its own.

I’ll cover both of these options in the section below.

## How to define typeclass instances

Let’s imagine we have a data type for Pokemon that includes their name, Pokedex number, type, and abilities.

```
data Pokemon = Pokemon
  { pokedexId :: Int
  , name :: String
  , pokemonType :: [String]
  , abilities :: [String]
  }

```

We have two Pokemon – Slowking and Jigglypuff – which are arguably the best offerings of the Pokemon universe.

```
*Main> slowking = Pokemon 199 "Slowking" ["Water", "Psychic"] ["Oblivious", "Own Tempo"]
*Main> jigglypuff = Pokemon 39 "Jigglypuff" ["Normal", "Fairy"] ["Cute Charm", "Competitive"]

```

For some reason, we would like to know whether their values are equal.

Right now, GHCi cannot answer this.

```
*Main> slowking == jigglypuff

<interactive>:19:1: error:
    • No instance for (Eq Pokemon) arising from a use of '=='
    • In the expression: slowking == jigglypuff
      In an equation for 'it': it = slowking == jigglypuff

```

That’s because `Pokemon` doesn’t have an instance of the `Eq` typeclass.

There are two ways of making `Pokemon` a member of the `Eq` typeclass: deriving an instance or manually creating it. I’ll cover them both.

### Deriving `Eq`

When creating a type, you can add the `deriving` keyword and a tuple of typeclasses you want the instance of, such as `(Show, Eq)`. The compiler will then try to figure out the instances for you.

This can save a lot of time that would be spent in typing out obvious instances.

In the case of `Eq`, we can usually derive the instance.

```
data Pokemon = Pokemon
  { pokedexId :: Int
  , name :: String
  , pokemonType :: [String]
  , abilities :: [String]
  } deriving (Eq)

```

The derived instance will compare two Pokemon for equality by comparing each individual field. If all the fields are equal, the records should be equal as well.

Now we can answer our question.

```
*Main> slowking == jigglypuff
False

```

### Defining `Eq`

The Pokedex number should uniquely identify a Pokemon (if we use the National Pokedex). So, technically, we don’t need to compare all the fields of two Pokemons to know that they are the same Pokemon. Comparing just the index will be enough.

To do that, we can create a custom instance of `Eq`.

First, we need to remove the `deriving (Eq)` clause.

```
data Pokemon = Pokemon
  { pokedexId :: Int
  , name :: String
  , pokemonType :: [String]
  , abilities :: [String]
  }

```

Then we can define an `Eq` instance for the `Pokemon` typeclass.

```
-- {1} {2}
instance Eq Pokemon where
  -- {3}
  pokemon1 == pokemon2 = pokedexId pokemon1 == pokedexId pokemon2

-- {1}: Typeclass whose instance we are defining.
-- {2}: The data type for which we are defining the instance for.
-- {3}: Method definitions.

```

Even though `Eq` has two methods: `==` and `/=`, we only need to define one of them to satisfy the minimal requirements of the instance.

#### Minimal requirements to define an instance

It usually isn’t necessary to define all the methods of the typeclass.

For example, `Eq` has two methods: `==` and `/=`. If you define `==`, it’s reasonable to assume that `/=` will be `not ==`.

In fact, that’s in the definition of the typeclass.

```
class Eq a where
    (==) :: a -> a -> Bool
    (/=) :: a -> a -> Bool
    x == y = not (x /= y)
    x /= y = not (x == y)

```

For this reason, the minimal definition for `Eq` is to define one of these methods.

To see the minimal definition of a typeclass, you can use `:info`.

```
*Main> :info Eq
...
  {-# MINIMAL (==) | (/=) #-}
...

```

If you provide the minimal implementation of a typeclass, the compiler can figure out the other methods.

This is because they can be:

- defined in terms of methods you’ve already provided;
- defined in terms of methods that a superclass of the typeclass has (I’ll cover superclasses later);
- provided by default.

You might want to provide your own implementations for performance reasons, though.

### Ordering Pokemon

Let’s imagine that we want to order and sort our Pokemon.

To compare two values of a type, the type needs to have an instance of the `Ord` typeclass.

```
class Eq a => Ord a where
  compare :: a -> a -> Ordering
  (<) :: a -> a -> Bool
  (<=) :: a -> a -> Bool
  (>) :: a -> a -> Bool
  (>=) :: a -> a -> Bool
  max :: a -> a -> a
  min :: a -> a -> a

```

`Ord` and `Eq` go hand in hand in Haskell since `Eq` is a superclass of `Ord`.

```
class Eq a => Ord a where           

```

Let’s quickly go over superclasses so that we understand what that means.

#### Superclasses

In Haskell, typeclasses have a hierarchy similar to that of classes in OOP.

If a typeclass `x` is a superclass of another class `y`, you need to implement `x` before you implement `y`.

In our case, we needed to implement `Eq` (which we did) before we implement `Ord.`

Furthermore, typeclasses often depend on their superclasses for method definitions. So you need to be careful that you “mean the same thing” when you define both the typeclass and its superclass.

For example, the `Ord` typeclass depends on the `Eq` typeclass for defaults, so it is a rule of thumb to have them be compliant with each other.

This means that our `Ord` instance should order things in a way that `a <= b` and `a >= b` implies `a == b`. Yeah, Haskell can be like that sometimes. 😂

* * *

Since we used the Pokedex number to define equality, we also will use it to define order.

In the case of `Ord`, the minimal definition is `<=` or `compare`. We’ll define the first of these.

Here’s how the instance definition looks:

```
instance Ord Pokemon where
  pokemon1 <= pokemon2 = pokedexId pokemon1 <= pokedexId pokemon2

```

At this point, it’s easy to see why our `Ord` instance needs to be compliant with our `Eq` instance. Since we provided only the minimal implementation, Haskell will use the method of `Eq` – `==` – and our definition of `<=` to create implementations for `<` and `>`.

Now we can compare Pokemon.

```
*Main> jigglypuff < slowking
True
*Main> jigglypuff > slowking
False

```

We can also create a third Pokemon and sort a list of Pokemon using the `sort` function.

To see the sorted list in GHCi, we need to derive the `Show` typeclass for our data type.

```
data Pokemon = Pokemon
  { pokedexId :: Int
  , name :: String
  , pokemonType :: [String]
  , abilities :: [String]
  } deriving (Show)

```

And now we can see the results:

```
*Main> chansey = Pokemon 113 "Chansey" ["Normal"] ["Natural Cure", "Serene Grace"]
*Main> import Data.List

*Main Data.List> sort([chansey, jigglypuff, slowking])
[Pokemon {name = pokedexId = 39, "Jigglypuff", pokemonType = ["Normal","Fairy"], abilities = ["Cute Charm","Competitive"]},Pokemon {pokedexId = 113, name = "Chansey", pokemonType = ["Normal"], abilities = ["Natural Cure","Serene Grace"]},Pokemon {pokedexId = 199, name = "Slowking", pokemonType = ["Water","Psychic"], abilities = ["Oblivious","Own Tempo"]}]

```

## Basic Haskell typeclasses

Let’s look at the basic Haskell typeclasses that you have encountered while reading this article.

### `Eq`

`Eq` provides an interface for testing for equality. It has two methods: `==` and `/=` for equality and inequality, respectively.

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

```

The minimal definition for `Eq` is to either provide `==` or `/=`.

You can generally derive this typeclass while defining your data types.

### `Ord`

`Ord` is a subclass of `Eq` that is used for data types that have a total ordering (every value can be compared with another).

```
class Eq a => Ord a where
  compare :: a -> a -> Ordering
  (<) :: a -> a -> Bool
  (<=) :: a -> a -> Bool
  (>) :: a -> a -> Bool
  (>=) :: a -> a -> Bool
  max :: a -> a -> a
  min :: a -> a -> a

```

It offers the following functions:

- `compare`, which compares two values and gives an `Ordering`, which is one of three values: `LT`, `EQ`, or `GT`.
- Operators for comparison: `<`, `<=`, `>`, `>=` that take two values and return a `Bool`.
- `max` and `min`, which return the largest and smallest of two values, respectively.

The minimal definition for the `Ord` typeclass is either `compare` or `<=`.

### `Show` and `Read`

`Show` is a typeclass for conversion to strings. `Read` is its opposite: it’s the typeclass for conversion from strings to values. The implementations are supposed to follow the law of `read . show = id`.

For beginners, the `show` method will be important for debugging purposes. If you are working with GHCi and need to print out your custom types in the terminal, you need to derive `Show`. Otherwise, you won’t be able to print them.

One important thing that some beginners get confused about: these typeclasses are not supposed to be used for pretty-printing and parsing complex values. There are better tools for that.

### `Num`

`Num` is the typeclass for numbers.

```
class Num a where
  (+) :: a -> a -> a
  (-) :: a -> a -> a
  (*) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a

```

The minimal definition for `Num` includes: `(+)`, `(*)`, `abs`, `signum`, `fromInteger`, and `negate` or `(-)`.

It offers all the arithmetic operations that you would expect to need when working with integers.

## Conclusion

This introduction to typeclasses in Haskell covered what typeclasses are and how to create your own instances by deriving or defining them.

For further reading, we have a series called [What’s That Typeclass](https://serokell.io/blog/what's-that-typeclass) that covers more advanced typeclasses. So far, we have posts about `Monoid` and `Foldable`, but more are to come.

If you want to get informed about new beginner-friendly Haskell articles, follow us on [Twitter](https://twitter.com/serokell) or subscribe to our mailing list via the form below.