Note on Parse, Don’t Validate via lexi-lambda.github.io
the difference between validation and parsing lies almost entirely in how information is preserved. Consider the following pair of functions:
validateNonEmpty :: [a] -> IO () validateNonEmpty (_:_) = pure () validateNonEmpty [] = throwIO $ userError "list cannot be empty" parseNonEmpty :: [a] -> IO (NonEmpty a) parseNonEmpty (x:xs) = pure (x:|xs) parseNonEmpty [] = throwIO $ userError "list cannot be empty"These two functions are nearly identical: they check if the provided list is empty, and if it is, they abort the program with an error message. The difference lies entirely in the return type:
validateNonEmptyalways returns(), the type that contains no information, butparseNonEmptyreturnsNonEmpty a, a refinement of the input type that preserves the knowledge gained in the type system. Both of these functions check the same thing, butparseNonEmptygives the caller access to the information it learned, whilevalidateNonEmptyjust throws it away.These two functions elegantly illustrate two different perspectives on the role of a static type system:
validateNonEmptyobeys the typechecker well enough, but onlyparseNonEmptytakes full advantage of it. If you see whyparseNonEmptyis preferable, you understand what I mean by the mantra “parse, don’t validate.”
Parsing returns a new piece of data. Parsing progresses the information processing, not just the program.
Reference
- Notes
- data, parsing, software, validation
- Parse, Don’t Validate
- 
        Permalink to 2023.NTE.501
- Insight
- Edit
| ← Previous | Next → | 
| Note on Parse, Don’t Validate via lexi-lambda.github.io | Note on Parse, Don’t Validate via lexi-lambda.github.io |