Summary
King (of Parse, Don’t Validate fame, now writing Scala at August Health on medication management software) argues that most of the value of static typing comes from three modest features, Product Types, Sum Types, and Exhaustive Pattern Matching, used with a constructive mindset rather than ever-fancier type system machinery. SSW 2026 conference talk, ~42 min including Q&A.
Key Claims
- Many people answer “what are types?” with “restrictions”, a view TypeScript makes literal: types as named subsets carved out of a universe of values (
unknown). But most languages instead let you construct new positive space from nothing; a class or record definition introduces a genuinely new kind of value. It’s easier to add than to subtract: define a type that only contains the values you want, rather than restricting a bigger one (which drags you toward refinement types and dependent types). - Decouple representation from interpretation. A non-empty list is a pair of (first element, rest); an even-length list is a list of pairs; a valid time range is (start, duration) instead of (start, end) with an invariant. There is no privileged “correct” representation of your data.
- Her answer to “what is the type system for”: it tracks the cases you have to handle. Type definitions relate producers and consumers that are far apart in the code; add a variant and exhaustiveness checking surfaces every consumer that needs updating. A type system is an “obligation propagation machine” (~16:40).
- Pick the simplest representation that lets you write Total Functions. Strengthen a type (e.g. list to non-empty list) only when an operation would otherwise need a panic/throw. Her inventory-log example: summing entries is fine with a plain list (empty sums to 0); “get most recent timestamp” is what justifies non-empty.
Option[T]vsT: which is more restrictive depends on use, not on the size of the value set. AcceptingOption[User]generously pushes an unhandleableNonecase into the consumer. Changing types moves failure-handling obligations to the place best equipped to handle them; this is the parse-don’t-validate move restated.- Corollary she pushes back on: readers of Parse, Don’t Validate who conclude they need an
EmailAddresstype everywhere. She represents emails as strings because no code inspects their structure. Types as simple as possible, but no simpler. - Fancy features (TS variadic tuple types, GADTs) are conveniences, not prerequisites. Their absence doesn’t make invariant-modeling impossible.
Notable Quotes / Excerpts
“It’s not really about trying to capture every invariant about your data in the types. It’s just the ones that allow you to avoid writing panic.” (~31:23)
My Reactions
- Constructive modeling means invariants by construction: build a positive space containing only valid values, using just Product Types, Sum Types, and Exhaustive Pattern Matching. No refinement or dependent types needed.
- The non-empty-list encoding trades interface compatibility for an unbreakable invariant; whether that trade is worth it depends on whether any operation would otherwise panic. Simplest representation that keeps functions total.
- Enum-of-valid-states beats a runtime validator (my instinct was a Pydantic model validator) not on elegance but on evidence: the validator checks and forgets, so after construction the type still claims both fields are optional and every consumer re-checks or trusts. The enum makes the invariant structural: the compiler tracks the cases and forces consumers to handle exactly them, and a new case later propagates as compile errors, not silent invariant drift.
- The unifying frame: types propagate obligations between producers and consumers, and redesigning a type is how you relocate failure handling to where it belongs.
Connections
- Parse, Don’t Validate, which this talk generalizes and, in places, corrects popular readings of
- Parse, Don’t Validate - Alexis King, the original 2019 blog post this talk builds on