Skip to Content
🇫🇷 🇪🇺 Looking for experienced guidance on Lean software delivery? We'd be glad to explore how we can work together. Contact →
Advanced Software EngineeringType StructuresAlgebraic Data Types: Precision and Expressiveness

Algebraic Data Types: Precision and Expressiveness

Algebraic data types (ADTs) form the foundation of modeling in typed functional languages, and their influence now extends far beyond.

An ADT is constructed by composing two fundamental operations:

  • The product (conjunction of fields, “A and B”)
  • The sum (disjunction of variants, “A or B”)

This simple algebra generates remarkable expressiveness. Where object-oriented languages offer classes with inheritance and interfaces, ADTs provide a more direct and rigorous approach to describing the exact shape of data that a program manipulates.

Product Types and Sum Types

Product types (records, structs, tuples) capture entities composed of multiple simultaneous attributes. A User has a name and an email and a createdAt. Nothing revolutionary here; all languages offer this construction.

The true power of ADTs lies in sum types: discriminated unions, variants, enums with associated data.

PaymentStatus = Pending | Completed { transactionId } | Failed { reason }

Each variant carries exactly the data relevant to its case. This modeling closes the space of possibilities: there is no fourth state, no hybrid combination.

Pattern Matching and Exhaustiveness

Pattern matching fully exploits this structure.

When deconstructing an ADT, the compiler requires exhaustiveness: each variant must be handled. Forgetting a case generates a compilation error, not a bug in production.

This static guarantee transforms risky refactorings into mechanical operations: add a variant to the sum type, then let the compiler guide you to all the places in the code that must be adapted. Add a Refunded status to your payment type: every switch that forgets it becomes a compilation error, fixed in minutes, rather than a bug discovered in production weeks later.

The type becomes a source of truth that automatically propagates changes throughout the codebase. Logic errors become type errors. This confidence in refactoring translates directly to velocity: the team can evolve the business model without fearing silent breakage of existing features.

Encoding Business Invariants

ADTs allow encoding business invariants directly into the data structure.

Rather than an Order with optional fields whose validity depends on a status field, we model an explicit state machine:

DraftOrder | ConfirmedOrder | ShippedOrder

each with its own shape.

The invalid state “shipped order without shipping date” becomes inexpressible: not forbidden by runtime validation, but structurally impossible. This is the essence of Make Illegal States Unrepresentable: ADTs provide the vocabulary for the type system to capture business rules, not just primitive types.

Growing Adoption

TypeScript has democratized ADTs in the JavaScript ecosystem via discriminated unions and automatic narrowing. Rust has made them idiomatic with its data-carrying enum and exhaustive match. Even Java, long limited to simple enums, has introduced sealed classes and pattern matching.

This convergence is not fortuitous: ADTs address a universal need for precise modeling. They reduce the gap between the developer’s thinking (“this value is either this or that”) and the code that expresses it.

By making the mental model explicit and verifiable, they eliminate an entire class of errors where code and intent silently diverge.

Want to dive deeper into these topics?

We help teams adopt these practices through hands-on consulting and training.

or email us at contact@evryg.com

Last updated on