Consider the following functions :

• `sum` : which takes a list of integers and returns their sum.

• `sumDifference` : which takes two lists of integers and returns the difference of the their sums.

• `sumNonEmpty` : which takes a list of integers and returns an `Option[Int]`, which is `None` if the list is empty, and a `Some` containing the sum of the members otherwise.

Suppose we want to generalize these functions to work on lists of some other type, such as `Double`s, pairs of integers i.e. `(Int, Int)` with `(x1, x2) + (y1, y2)` defined as `(x1 + y1, x2 + y2)`, or pairs of `Double`s. Take a moment and think about how you would do it. All these types seem to naturally support the operations addition and subtraction, but this fact is not encoded in their inheritance hierarchy, which makes it extremely difficult to write generic functions that work for all of them.

To solve this problem, let’s define a trait called `Group` :

`Group` is a generic trait, which takes a type parameter `T` and declares the operations plus, minus and inverse for objects of type `T`, as well as an element zero. Note that minus is defined using plus and inverse because `x - y = x + (-y)`(how clever!).

Groups are a real thing, and the operation plus needs to be associative i.e. `(x + y) + z` should be equal to `x + (y + z)`, otherwise we may get weird results.

Instances of the trait `Group` should ideally be defined as implicit members of the object `Group`, which lives is the same file as the trait. It is called the companion object of the trait. When the Scala compiler needs to supply an implicit parameter of type `Group[T]`, it looks inside the `Group` companion object. So, we won’t need to pass around `Group` instances explicitly while writing and using functions that depend on group operations.

Let’s define an instance of the trait for integers (inside the `Group` object, of course):

The instance for `Double` looks almost identical, except for the types :

Let’s try something a little more interesting. Here’s a method that produces a an instance of `Group` for the pair `(T1, T2)`, if it can find instances of `Group` for `T1` and `T2` :

This single method will automatically provide `Group` instances for `(Int, Int)`, `(Int, Double)`, `(Double, Int)` and `(Double, Double)` whenever we need them. Isn’t that neat?

Alright, now that we’ve done all the hard work, let’s use the trait to make our `sum` functions generic :

With these small modifications, you can now use these functions on lists of any type `T` for which the compiler can find an implicit object of type `Group[T]`:

That’s it! That’s the type class pattern. `Group` is called a type class (because it represents a class of types that support group operations), and the types `Int`, `Double`, `(Int, Int)` etc. are called members of the type class `Group` because the compiler can find instances of the type class for these types.

This post merely scratches the surface of the use cases and benefits of using type classes in Scala. Type classes are awesome and you should use them all over your code base. There are many great resources (much better than this one) for learning about type classes in Scala, like this, this and this. The only reason this post exists is so that I can point out the deficiencies in this naive implementation, and show you how to make it much better in my next post.