Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk4 min

ADTs and Enums in Scala 3: Make Invalid States Harder to Represent

Scala 3 enums and sealed traits model closed alternatives and help the compiler flag omitted match cases. Learn how to choose between them and where their guarantees stop.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle every case with pattern matching

Pattern-match on the ADT when behavior differs by alternative:

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How 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.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.