Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Scala 3 enums and sealed algebraic data types (ADTs) let you model a value as one of a known set of alternatives, then have the compiler warn when a pattern match omits a known case. They can make selected invalid combinations unrepresentable—but they do not validate arbitrary input or enforce every business rule. The examples below use Scala 3 enum and sealed-type syntax; exhaustivity diagnostics are warnings unless your build configures warnings to fail the build.
What an ADT gives you
An algebraic data type represents a value using alternatives, often called a sum type. Instead of one broad record with fields that only make sense in certain combinations, each alternative can carry the data relevant to that case. Scala 3 enums support both named values and parameterized cases, making them a direct way to define many such models. The Scala 3 Book explains enums as a way to create ADTs and GADTs, and shows how matching a constructor can provide type information for that case: Algebraic Data Types.
As an Amazon Associate I earn from qualifying purchases.
A simple enum for a fixed set
When the alternatives are just named values, a simple enum is often the clearest model:
enum Color:
case Red, Green, Blue
A value of type Color is one of those three cases. Code that receives a Color can match on the alternatives directly.
#1 Best Overall
Parameterized cases for alternatives with data
When each alternative has different relevant data, attach it to that case. For example:
enum PaymentStatus:
case Pending
case Paid(receiptId: String)
case Declined(reason: String)
This model does not expose a receipt identifier on a pending payment or a decline reason on a paid payment. A single record with a status string and nullable fields could represent those nonsensical combinations; the ADT does not provide those fields as part of the wrong alternative.
Rank #2
This is a structural guarantee, not a complete business-rule engine. The type above does not prove that a receipt identifier is non-empty, that a payment has been authorized, or that an incoming status string is valid. Parse and validate data from databases, network calls, and other untrusted boundaries before creating trusted domain values.
Free tools Windows power users keep installed
One-click scans. No signup required.
Handle every case with pattern matching
Pattern-match on the ADT when behavior differs by alternative:
Rank #3
def message(status: PaymentStatus): String =
status match
case PaymentStatus.Pending => "Payment is pending"
case PaymentStatus.Paid(receipt) => s"Receipt: $receipt"
case PaymentStatus.Declined(why) => s"Payment declined: $why"
Because the enum defines a closed set of cases, the compiler can check whether the match covers them. If you later add another case—for example, Refunded—the match may need a new branch. Scala’s E029 diagnostic reports non-exhaustive pattern matches as a warning; whether that warning fails a build depends on the project’s compiler configuration. See E029: Pattern Match Exhaustivity.
Prefer explicit cases when behavior matters
Listing cases explicitly makes the intended behavior visible and gives exhaustivity checking a chance to flag a newly added alternative. A wildcard branch can cover anything left over:
status match
case PaymentStatus.Pending => "Waiting"
case _ => "Finished"
That fallback may be appropriate when all remaining cases truly share the same behavior. But it also means a newly added case can fall into the wildcard rather than prompting a deliberate decision. Use it when that trade-off is intentional, not just to silence a warning.
When to use an enum or a sealed trait
Both enums and sealed traits can express closed alternatives. Choose based on the shape and organization of the model:
| Need | Suitable design | Why |
|---|---|---|
| A small fixed set of named values | Simple enum | It communicates the finite choices directly. |
| Alternatives with different associated fields | Parameterized enum | Each case keeps its relevant data attached, and constructor patterns support case-specific matching. |
| A closed family organized as explicit case classes or objects | Sealed trait with cases in the same source file | The closed set of direct subtypes allows exhaustivity analysis. |
| A hierarchy that must allow external direct subtypes | Do not use a sealed base or enum as the extension point | Scala restricts sealed direct subtypes to the base’s source file and prevents external extension of enums. |
A sealed-trait alternative
If you prefer to define the cases as separate case classes or objects, a sealed trait can be the family base:
sealed trait PaymentStatus
case object Pending extends PaymentStatus
case class Paid(receiptId: String) extends PaymentStatus
case class Declined(reason: String) extends PaymentStatus
The direct subtypes of a sealed base must be declared in the same source file. That restriction is what lets the compiler reason about the known family. The Scala documentation describes the connection between a sealed base and exhaustivity checking in Pattern Matching | Tour of Scala. If external modules must add direct subtypes, reconsider whether a closed hierarchy is the right design; the same-file restriction is documented in E125: Class Cannot Extend Enum.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.What the compiler guarantee does—and does not—cover
- It can flag an omitted known alternative. For a match on a closed enum or sealed hierarchy, Scala can report cases it believes are missing.
- It does not validate values before they enter the model. Convert and validate external input at the boundary; a type declaration cannot make malformed serialized data valid.
- It does not enforce every invariant. A case can still contain a value that violates a rule unless your constructors or validation logic prevent it.
- It does not always make the warning a build failure. E029 is an exhaustivity warning; check the compiler options and warning policy for the project in question.
Pattern-binding behavior and static checks can vary with Scala version and source settings. The Scala reference discusses pattern bindings in the context of Scala 3.2 and -source future, and documents runtimeChecked as an explicit mechanism that exempts an expression from certain static checks, including exhaustivity checking. See Pattern Bindings and The runtimeChecked method. For reproducible examples and reliable build behavior, pin the Scala compiler version and relevant source and warning settings.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchesHow enums are represented
Scala 3 enum syntax is translated into ordinary Scala constructs: a sealed abstract class and companion object, with cases represented according to whether they are singleton cases or parameterized class cases. Most application code can work directly with enum syntax; the translation matters mainly when reasoning about interoperability or representation details. The Scala reference describes those rules in Translation of Enums and ADTs.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




