Parse, Don’t Validate
Alexis King ’s article, published in 2019, crystallized an intuition that many functional programmers carried without necessarily knowing how to articulate it.
The Central Thesis
The thesis is crystal clear: validation is an operation that inspects data and responds with a boolean, then immediately forgets everything it just learned. Parsing, on the other hand, transforms weakly typed data into strongly typed data, thus preserving the acquired information in the type system.
This seemingly subtle distinction has profound architectural consequences.
The Non-Empty List Example
King illustrates her point with the example of a function that verifies a list is non-empty before extracting its first element.
- The validating approach returns a
boolean, forcing the calling code to then blindly trust again or re-verify. - The parsing approach directly returns a
NonEmptyListtype that structurally proves the list contains at least one element.
The head then becomes a total operation, with no risk of exception. We no longer ask “is this valid?” but “what is the typed representation of this data?”, and failure becomes explicit via a type like Option or Either.
Link to “Make Illegal States Unrepresentable”
This philosophy is a direct continuation of Yaron Minsky’s Make Illegal States Unrepresentable, but addresses the dynamic aspect.
Where Minsky speaks of type structure, King speaks of the system’s boundary: that moment when untrusted data (user inputs, API responses, configuration files, etc.) enters our domain. Parsing constitutes this translation membrane that transforms external chaos into first-class citizens of our domain model, with all the guarantees that implies.
Trust Layer Architecture
In practice, adopting this approach leads to rethinking architecture in terms of trust layers:
- At the periphery, we accept
unknownor broad types likestring - As soon as possible, we parse toward precise business types:
UserId,PositiveInteger,ValidatedEmail - Once in the domain core, we exclusively manipulate these refined types
The compiler then guarantees that no unvalidated data can slip in. Smart constructors and opaque modules become the preferred tools for this strategy, exposing only parsing functions that return guaranteed types.
Impact on Maintainability
The impact on maintainability is considerable. Code becomes correct by construction rather than correct by convention:
- We no longer need to wonder if a given function was called after a certain validation: the type attests to it
- Tests can focus on business logic rather than defensive verification of preconditions
- Refactorings become safer: if we forget to handle a case, the compiler signals it immediately
Alexis King thus reminds us that types are not just a convenience for autocompletion: they are a formal language for expressing and verifying our system’s invariants.
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