perhaps it is too early to generalize, but it seems to me that type classes basically allow you to define methods on classes you don’t own
Here is a simple example of one very useful thing you can do with a type class
trait Semigroup[A] {
def combine(l: A, r: A): A
}
implicit val intSemigroup = new Semigroup[Int] {
def combine(l: Int, r: Int): Int = l + r
}
implicit val stringSemigroup = new Semigroup[String] {
def combine(l: String, r: String): String = l + r
}
intSemigroup.combine(2,3)
stringSemigroup.combine("2","3")
def combine3[A](x: A, y: A, z: A)(implicit sg:Semigroup[A]): A =
sg.combine(sg.combine(x,y),z)
combine3(2,3,4)
combine3("2","3","4")
def combineAll[A](as: Seq[A])(implicit sg:Semigroup[A]): A =
as.reduce(sg.combine(_,_))
combineAll(Seq(2,3,4))
combineAll(Seq("2","3","4"))
implicit def listSemigroup[A] = new Semigroup[List[A]] {
def combine(l: List[A], r: List[A]): List[A] = l ++ r
}
combineAll(Seq(List(2,3), Nil, List(4)))
implicit def optionSemigroup[A] = new Semigroup[Option[A]] {
def combine(l: Option[A], r: Option[A]): Option[A] = l orElse r
}
combineAll(Seq(Some(2), None, Some(3)))
combineAll(Seq(None, None, Some(3)))