🎓 A1Learner
🔍
0 of 48 lessons completed

Scala Sealed Hierarchies

A sealed trait is a trait with a closed set of subtypes. The compiler therefore knows every subtype when it checks a match.

A sealed match covers the rectangle

Shape has two subtypes in this program. The rectangle case gives its area.

sealed trait Shape
case class Circle(r: Double) extends Shape
case class Rect(w: Double, h: Double) extends Shape
@main def main(): Unit =
  val s: Shape = Rect(2.0, 3.0)
  val area = s match
    case Circle(r) => 3.14 * r * r
    case Rect(w, h) => w * h
  println(area)

The same match covers the circle

The circle case uses the same sealed match. No catch-all case is needed.

sealed trait Shape
case class Circle(r: Double) extends Shape
case class Rect(w: Double, h: Double) extends Shape
@main def main(): Unit =
  val s: Shape = Circle(2.0)
  val area = s match
    case Circle(r) => 3.14 * r * r
    case Rect(w, h) => w * h
  println(area)

A third subtype adds a third case

Adding Triangle adds one more subtype. The match gains one more case to stay exhaustive.

sealed trait Shape
case class Circle(r: Double) extends Shape
case class Rect(w: Double, h: Double) extends Shape
case class Triangle(b: Double, h: Double) extends Shape
@main def main(): Unit =
  val s: Shape = Triangle(4.0, 5.0)
  val area = s match
    case Circle(r) => 3.14 * r * r
    case Rect(w, h) => w * h
    case Triangle(b, h) => b * h / 2
  println(area)

Example

sealed trait Shape
case class Circle(r: Double) extends Shape
case class Rect(w: Double, h: Double) extends Shape
def area(s: Shape): Double = s match
  case Circle(r) => 3.14 * r * r
  case Rect(w, h) => w * h
@main def main(): Unit =
  println(area(Circle(2.0)))
  println(area(Rect(2.0, 3.0)))

Key points

  • Sealed trait: a trait whose subtypes form a closed set that cannot grow by surprise.
  • Use a sealed trait for a small set of kinds that belong together.
  • Each subtype is a case class or a case object that extends the sealed trait.
  • A match over a sealed trait needs a case for every subtype.
  • With every case covered, no catch-all case is needed.

In this example, area matches over the sealed Shape trait. It has a case for Circle and a case for Rect, so both calls run. The two printed areas prove both cases work.

Note

a missing case throws MatchError at run time if that value arrives.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Scastie

Exercise

Shape is sealed with subtypes Circle and Rect. Which match is exhaustive with no catch-all?