Validation discards information; parsing preserves it. A classical validator returns () on success and throws or short-circuits on failure: a yes/no with no payload. Downstream code therefore sees the same input type it started with and gains nothing from the check. A parser instead returns a refined type that encodes the invariant in its structure: parseNonEmpty :: [a] -> Maybe (NonEmpty a) hands back a list whose type witnesses non-emptiness, so head becomes total and redundant re-checks vanish.
Alexis King (2019) names the alternative “shotgun parsing”: invalid input partially mutates state before failure surfaces, a class of bug that LangSec has tied directly to security CVEs. The principle generalizes past Haskell. Branded types in TypeScript, Pydantic models in Python, and smart-constructor abstract types in any language are the same trick: each is a way to Make Illegal States Unrepresentable.
The operational rule is “parse once, eagerly, at the boundary.” That sharpens the folk DDD advice “validate at the boundary,” because it forces the boundary to produce a different type whose existence proves the check ran. See Parse, Don’t Validate - Alexis King for the full argument and the seven design heuristics King derives.