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.

/**
  * Scala Type Class
  * It allows you to take a class and through ad-hoc polymorphism add operations
  * which will work on that class without altering that class at all.  Also, leaving the
  * type class open for extension by anyone in the future
  *
  * So, for my example, you have modeled some items in a kitchen.
  * Then, some time in the future you decide that two creatures are fighting, and that
  * they can pick up the kicthen stuff and use it as weapon.
  * Normally, the kitchen classes have nothing about fighting in, so rather than couple
  * them to creatures fighting, lets use a type class!
  */
class FryingPan {/* ... */}
class WoodenBreadBoard {/* ... */}
//... etc

// Now for the fighting stuff, creatures have health and take damage
case class Creature(var name:String, var health:Int) {
  def inflictDamange(severity:Int) = health -= severity
}

// 1. Define the type class - ie ad-hoc polymorphism using
trait AdHocWeapon[A] {
  // 2. add some operations you want for this thing
  def attack(weap: A, target:Creature)
}

// 3. provide a companion object holding an object or a val with some implementations
// Scala implicit resolution will find the one in scope which matches
// This companion shows actual example of converting a type into the operation attack.
object AdHocWeapon {
  // This is part of my library, and others can add their own or override it later
  implicit object FryingPanAdHocWeapon extends AdHocWeapon[FryingPan] {
    def attack(weap: FryingPan, target:Creature) = target.inflictDamange(20)
  }
  implicit object WoodenBreadBoardAdHocWeapon extends AdHocWeapon[WoodenBreadBoard] {
    def attack(weap: WoodenBreadBoard, target:Creature) = target.inflictDamange(2)
  }
}

// 4. Code against the type class
object FightInTheKitchen {
  // W:AdHocWeapon is context bound, saying W will extend AdHocWeapon
  def fightOneRound[W1:AdHocWeapon, W2:AdHocWeapon](beast2Weap:W1, beast1:Creature, beast1Weap:W2, beast2:Creature) = {
    // 5. implicitly find the  object with the function that matches the type
    implicitly[AdHocWeapon[W1]].attack(beast2Weap, beast1)
    implicitly[AdHocWeapon[W2]].attack(beast1Weap, beast2)
  }
}

// 6. Now I can fight using frying pans and breadboards neither of which are weapons.
object demoFight extends App {
  val vampire = Creature("Vampire", 10000)
  val hobbit = Creature("hobbit", 10)

  println(s"$vampire vs $hobbit")
  FightInTheKitchen.fightOneRound(new FryingPan, vampire,new WoodenBreadBoard, hobbit)
  println(s"$vampire vs $hobbit after round 1")
}

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.

trait KitchUtensil
class FryingPan extends KitchUtensil {/* ... */}
class WoodenBreadBoard  extends KitchUtensil {/* ... */}
//... etc

case class Creature(val name:String, var health:Int) {
  def inflictDamage(d:Int) = health -= d
}

class Weapon(val damage:Int)
case object Knife extends Weapon(10)
case object Axe extends Weapon(25)
// .. etc

// Being a library designer I want to allow other kinds of weapons
// and they should all have a function which adapts them to be normal weapons
trait OtherWeapon[T] {
  def weapon : Weapon
}
object OtherWeapon {
  implicit val FryingPanWeapon = new OtherWeapon[FryingPan] {
    def weapon = new Weapon(9)
  }
  implicit val WoodenBreadBoardWeapon = new OtherWeapon[WoodenBreadBoard] {
    def weapon = new Weapon(1)
  }
}

object EpicBattle extends App {
  def resolve(attackingWeapon:Weapon, targetCreature:Creature) : Unit = {
    targetCreature.inflictDamage(attackingWeapon.damage)
  }
  // and I design my library with the extensible type
  def resolve[W:OtherWeapon](attackingWeapon:W, targetCreature:Creature) :Unit = {
    resolve(implicitly[OtherWeapon[W]].weapon, targetCreature)
  }

  // the elf and orc fight in the woods with weapons
  val orc = Creature("Orc", 80)
  val elf = Creature("Elf", 110)
  println(s"Before $orc vs $elf")
  resolve(Axe, elf)
  resolve(Knife, orc)

  // The vampire and hobbit fight in the kitchen with utensils
  val vampire = Creature("Vampire", 10000)
  val hobbit = Creature("hobbit", 10)
  println(s"Before $vampire vs $hobbit")
  resolve(new FryingPan, vampire)
  resolve(new WoodenBreadBoard, hobbit)

  println(s"After $orc vs $elf")
  println(s"After $vampire vs $hobbit")
}

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.