Reading — step 1 of 5
Learn
~1 min readImplicits and Type Classes
A type class is an interface defined separately from the types that implement it. Combined with implicits, you can retroactively make any type satisfy it.
// 1. Define the type class
trait Show[A] {
def show(a: A): String
}
// 2. Provide instances for the types you care about
implicit val intShow: Show[Int] = new Show[Int] {
def show(a: Int): String = s"int: $a"
}
implicit val strShow: Show[String] = new Show[String] {
def show(a: String): String = s"str: '$a'"
}
// 3. Generic function uses the type class via implicit
def display[A](a: A)(implicit s: Show[A]): String = s.show(a)
display(42) // "int: 42"
display("hi") // "str: 'hi'"
Context bound is shorthand for (implicit ev: TypeClass[A]):
def display[A: Show](a: A): String = implicitly[Show[A]].show(a)
This is THE pattern in Scala libraries — Ordering[A], Numeric[A], Ordering[A], Equiv[A]. Spark, Cats, ZIO, Akka — all built on type classes.
Why over inheritance?
- Add behavior to types you don't own (Java strings, Int, etc.)
- Multiple instances per type (different orderings, etc.)
- Compose without forcing every type into a hierarchy
With Scala 3 you'd write:
given Show[Int] with
def show(a: Int): String = s"int: $a"
— but the underlying machinery is identical.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…