Scala DISCUSSION

What does sealed do on a Scala trait, and how does it stop a MatchError at runtime?

Started by emadfadaee Scala sealed traitexhaustive pattern matchingMatchErroralgebraic data typesScala 3 enum
4 replies 248 views 5 participants
Latest activity · 30 Sep 2026

What does sealed do on a Scala trait, and how does it stop a MatchError at runtime?

emadfadaee Scala Forum
#1

My motor controller code models commands as trait Command with case class SetSpeed(rpm: Int), case object Stop and case object Reset, handled in one match. Last week a colleague added case object Calibrate and forgot the handler; everything compiled, and the program threw scala.MatchError in the field.

I was told that making the trait sealed would have caught this. What does sealed actually change, and are there cases it does not cover?

Community replies 4

Re: What does sealed do on a Scala trait, and how does it stop a MatchError at runtime?

#2

sealed means that all direct subtypes of the trait must be defined in the same source file. Outside that file nobody can extend it. Because the compiler then knows the complete list of alternatives, it can check every match on a Command and warn when one is missing, naming the input that would fail, here Calibrate.

With a non-sealed trait the compiler has to assume that further subclasses may exist elsewhere, so an incomplete match is accepted silently and fails only when the unhandled value arrives.

Re: What does sealed do on a Scala trait, and how does it stop a MatchError at runtime?

#3

By default the missing case is a warning, and warnings get ignored. Turn them into errors in the build with the -Werror compiler option (called -Xfatal-warnings in older compilers) so that the build breaks when someone adds a fourth command without a handler.

Do not add a catch-all case _ => to silence the warning. It makes the match formally complete, which disables the check exactly when you need it: the next new command will fall into the default branch without any diagnostic.

Re: What does sealed do on a Scala trait, and how does it stop a MatchError at runtime?

#4

Limits worth knowing. sealed restricts only direct subtypes. If one of them is an ordinary open class, that class can still be extended in other files, so mark the leaves final case class or use case object. Guards also weaken the check: a branch like case SetSpeed(rpm) if rpm > 0 may not match every SetSpeed, so the compiler does not count it as covering that type, and you need an unguarded case SetSpeed(rpm) after it.

The check applies when the type being matched is the sealed type. If the value is typed as Any, there is nothing to check against.

Re: What does sealed do on a Scala trait, and how does it stop a MatchError at runtime?

#5

This pattern, a sealed parent with case classes and case objects as the only children, is how Scala expresses an algebraic data type, and the standard library uses it throughout: Option is sealed with Some and None, which is why matching only on Some(x) produces the same warning.

In Scala 3 you can write the hierarchy more compactly as enum Command { case SetSpeed(rpm: Int); case Stop; case Reset; case Calibrate }, with the same exhaustiveness checking. The trade-off against virtual methods is the usual one: with a sealed type, adding a new operation is one new function, while adding a new case means visiting every match, and the compiler lists them for you.

TEP COMMUNITY