Concept
Algebraic Data Types and Pattern Matching
Algebraic data types model values as products and alternatives, while pattern matching lets code branch by the shape of those values and often check that all cases were handled.
Related languages
Products And Sums
Algebraic data types give names to combinations of values.
Product types combine fields that exist together: records, structs, tuples, classes, and object shapes can all play this role. A User with an id, email, and status is a product shape.
Sum types describe alternatives: a value is one of several variants. Haskell data declarations, OCaml variants, F# discriminated unions, Rust enums, Swift enums with associated values, Kotlin sealed classes or interfaces, and Scala sealed hierarchies all support this style in different forms.
Pattern Matching
Pattern matching lets code branch by the shape of a value while binding the parts it needs. A match can say: if this is a success, bind the value; if it is an error, bind the reason; if it is a known command, extract the argument; if it is none of the above, use a fallback.
The strongest systems can check exhaustiveness. If a sum type has three variants and a match handles only two, the compiler can warn or reject the code. That is valuable for protocols, parsers, command handlers, UI states, payment flows, job states, and compiler-like programs where forgotten cases become bugs.
Approximations
Languages without native algebraic data types still approximate the pattern:
- TypeScript commonly uses discriminated unions over object shapes.
- Java and C# use sealed classes, records, enums, pattern matching features, and visitor-style APIs.
- Go often uses interfaces plus type switches or explicit tagged structs.
- Python and Ruby use tagged objects, dataclasses, hashes, classes, and runtime pattern matching or case logic.
The approximation can be good enough, but the checker may not know every possible case unless the language gives it a closed set of variants.
Watch Points
Algebraic data types are best when the domain really is a fixed set of shapes. They are less natural when new variants must be added by third-party plugins without changing the original definition.
Pattern matching can also become too broad. A large match expression may be clearer as several smaller functions, methods, visitors, or table-driven handlers. Use matching where it makes states explicit; avoid turning every branch in the program into one central switch.
Sources
Last verified:
- Enums and Pattern Matching - The Rust Programming Language Rust Project
- Patterns and Matching - The Rust Programming Language Rust Project
- Haskell 2010 Language Report - Declarations and Bindings Haskell.org
- The OCaml Language - Types OCaml
- Discriminated Unions - F# Microsoft Learn