Having worked without them for years it took some time to work out why we use them. So here is my attempt to explain it, without using NumberLike or Json.
Why Type Classes
Ad-hoc polymorphism. You have a bunch of classes and you want to create a bunch of new operations which cut over some of them. Scala has special syntax which makes type classes first class citizens of the language. ie use this syntax to decouple classes, its core to the way we all should code in Scala.
My example is not using Algebraic Data Types or Json. Instead, lets say you have some objects in a kitchen and you modelled them all for cooking. Then you decide to make a monster game, and they are fighting in the
kitchen and can grab stuff and attack each other with them. So, I’m going to add an attack operation to some of my kitchen implements by using a new AdHocWeapon data type.
As clever Chris Wong said - a Type Class simply converts one type to another.
Fighting with kitchen stuff
Here is the simple example. We are creating a new Type Class called an AdHocWeapon, and it has an attack operation.
Recap on the key points
Ad-hoc polymorphism - the Scala way to leave decouple classes. If you have classes in one domain which have nothing to do with a new set of operations in a different domain, use Type Classes.
The type class simply defines the new operations in the new domain you want - a typed base trait, and some implicit objects or vals which Scala can infer from the types, providing they are in scope, and there is only one that matches.
The companion object includes the initial implementations you want. Other devs can alter these or add more later on. The companion object effectively converts the classes you have already to extend the type class and
provide the implementation.
You define the objects and vals in the companion object to be implicit so they can be pulled in by the type.
When using the type class, you should use a ‘Context bound type’ which is of the form def function[A:NameOfTypeClass], so we are saying that A is going to extend NameOfTypeClass, and that it will be pulled in from an implicit object or val which can convert it.
implicitly[AdHocWeapon[W2]] is sadly the syntax to invoke the new operations (methods) in the type class implementation. eg implicitly[AdHocWeapon[W2]].attack
Vampires fighting with bread boards still beat hobbits fighting with frying pans.
Type classes convert classes?
The example above I describe as adding methods, but I am passing in the weapon to the attack function, but the damage is fixed by the weapon type. Not only that, but I said to start with that type classes let you add operations to existing classes which is not what most articles say. I read quite a few blogs and watched talks, and I think the example above is simpler. However…
Take 2 - a library designed for Type Classes
Lets rewrite it, this time we have two domains, a kitchen with utensils, and then fantasy fighting which has creatures and weapons.
Presumably, during my library design I have pure weapons, but accept some sort of ‘OtherWeapon’ will be used. So the Type Class OtherWeapon is born and a battle resolution function is written for Weapon and also
for OtherWeapon. This small expenditure up front lets anyone write an additional implicit OtherWeapon converter.
The output from this is:
Before Creature(Orc,80) vs Creature(Elf,110)
Before Creature(Vampire,10000) vs Creature(hobbit,10)
After Creature(Orc,70) vs Creature(Elf,85)
After Creature(Vampire,9991) vs Creature(hobbit,9)
Weapon and OtherWeapon?
I could do another example, where I have Weapons, Utensils and then a type class which could be OffensiveWeapon. I could then add implicit to convert both to OffensiveWeapon, and have my battle module use OffensiveWeapon. I won’t do it now, this post is already too long. However, I think this would be a better design, as Weapons could be for status, defence or ornament.