A
Alexis King
lexi-lambda.github.io · Personal blog · on HN since 2015
Alexis King writes about Haskell, type systems, and programming language design, and she created the Hackett language.
What HN saysCommenters repeatedly point to 'Parse, Don't Validate' as classic must-read, and call her an excellent programmer and great writer.
172HN people shared it
132linked it in comments
49stories submitted
3.4Kstory points
3 months agolast shared
2015people per year2026
Most-shared posts
- 1Parse, Don’t Validate99 people · 1.4K points · first shared 2019 · 270 comments on HN
- 2Dynamic type systems are not inherently more open17 people · 386 points · first shared 2020 · 275 comments on HN
- 3Realizing Hackett, a metaprogrammable Haskell8 people · 161 points · first shared 2017 · 32 comments on HN
- 4Names are not type safety6 people · 318 points · first shared 2020 · 125 comments on HN
- 5An opinionated guide to Haskell6 people · 263 points · first shared 2018 · 92 comments on HN
- 6The Hackett Programming Language6 people · 3 points · first shared 2018 · discussion on HN
- 7Types as axioms, or: playing god with static types5 people · 114 points · first shared 2020 · 25 comments on HN
- 8A Break from Programming Languages5 people · 32 points · first shared 2025 · 4 comments on HN
- 9Empathy and subjective experience in programming languages5 people · 23 points · first shared 2019 · 3 comments on HN
- 10Rascal: a Haskell with more parentheses5 people · 11 points · first shared 2017 · 1 comments on HN
- 11Climbing the infinite ladder of abstraction4 people · 81 points · first shared 2016 · 24 comments on HN
- 12Simple, safe multimethods in Racket3 people · 53 points · first shared 2016 · discussion on HN
- 13Four months with Haskell2 people · 249 points · first shared 2016 · 117 comments on HN
- 14An Introduction to Typeclass Metaprogramming2 people · 75 points · first shared 2021 · discussion on HN
- 15Understanding the npm dependency model2 people · 1 points · first shared 2016 · discussion on HN
What HN people wrote when they linked it
girvo · 2021-01-02
I highly recommend people check out Alexis’ other projects too. An excellent programmer, and great writer.
https://lexi-lambda.github.io
yakshaving_jgt · 2026-02-10
> Possible! But a little hyperbolic, perhaps. I think we're more likely to be talking past one another.
Direct, certainly. More than would be typically expected socially. But I don't think it's hyperbolic — I think there is genuine fundamental misunderstanding here.
The best source which I believe dispels your misunderstanding is this one: https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-…
myvoiceismypass · 2025-12-02
'Parse, don't validate' is such great reading: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
ebonnafoux · 2025-11-07
The very classic parse don't valide if you haven't already read it : https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
AdieuToLogic · 2025-10-11
While the classic "Parse, don’t validate"[0] paper uses Haskell instead of C as its illustrative programming language, the approach detailed is very much applicable in these scenarios.
0 - https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
3eb7988a1663 · 2025-07-26
Do you mean like is the User object is a well formed User, or did someone actually give you an int?
As to the first problem, I recommend the Parse don't validate post[0]. The essential idea is stop using god objects that do it all, but use specific types to make contracts on what is known. Separate out concerns so there is an UnvalidatedUser (not serialized and lacking a primary key) and a ValidatedUser (committed to the database, has unique username, etc). Basic type hinting should get you the rest of the way to cleaning up code paths where you get some type certainty.
[0] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
azaslavsky · 2025-05-17
Couldn't you just take the advice in [0] of parsing into types rather than validating? Then you get the best of both worlds: your inputs are necessarily checked every time the function is called (they would have to be to create the type in the first place), but you don't need to validate them at every nested layer. You also get the benefit of more descriptive function signatures to describe your interfaces.
[0] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
manx · 2025-01-15
I recommend reading: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
tbct · 2025-01-04
Surely, however from a software architecture perspective types as contracts, assuming pure functions and maximally restrictive types, is a good model for reasoning about a system. A function such as:
addIfEven: Int -> Int -> Maybe Int
even without knowledge of the implementation lets us know our caller has 'entered into a contract' where addIfEven will reduce two integers depending on some conditions. This contract then becomes two-wayed: If I modify the implementation of the function there's a good chance I'll be forced to modify the signature, thereby letting me know statically that I've broken the contract with the callsite.
I'd recommend checking out https://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playi… for a far better write-up than this comment on why type-driven development is like unlocking a cheat-code for development.
oasisaimlessly · 2024-07-23
The reference (an excellent read!): "Parse, don't validate" https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
jmduke · 2024-03-11
Agreed with this essay, and I think it rhymes with two others that I've found pretty influential over the past five years:
1. Parse, don't validate (https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)
2. Pipeline-oriented programming (https://fsharpforfunandprofit.com/pipeline/)
In my experience, the "best" code (defining "best" as some abstract melange of "easy to reason about", "easy to modify", "easy to compose", and "easy to test") ends up following the characteristics outlined by the sum of these three essays — strictly and rigorously elevating exceptions/failures/nulls to first-class types and then pushing them as high in the stack as possible so callers _must_ deal with them.
cbdumas · 2023-10-04
> we actually follow deserialization is validation (is that a term?)
Perhaps you are thinking of this great blog post on the subject https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
armchairhacker · 2023-08-15
This is a nice explanation, but why is it written on stack exchange and not (also?) https://lexi-lambda.github.io/?
lexi-lambda · 2023-03-08
Yes, certainly. Constructive data modeling is useful for many things, but it’s not a panacea. Other techniques are useful in cases where it’s not practical; I discuss some of the tradeoffs in this followup post: https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safet…
conaclos · 2023-03-07
The same author wrote a follow-up article [0] "Names are not type safety".
[0] https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safet…
goostavos · 2022-07-17
This blog post really captures the core of where null checking should go and how to capture that you've already vetted this field for correctness in the type system so that the rest of your code never has to worry about it -- and further, cannot because the types don't allow!: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
This is echoed in an amazing book called Domain Design Made Functional, which radically changed how I thought about what a type system is and what it can actually do for us if we lean on it correctly (even a relatively crummy one like Java's!).
Zababa · 2021-08-02
Usually the approach in a statically-typed language is to transform your dynamic request into something that you know through parsing instead of validation. Here's a great article about this: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/.
runeks · 2021-06-27
> There's a follow-up article by the same author (that I unfortunately can't find), in which she explains this point.
I think this is it: https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safet…
ookdatnog · 2020-08-18
I recommend reading this article that's linked in the evidence section: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
tpaschalis · 2020-03-12
An excellent article on type-driven design and development was posted on HN [1][2], as darkkindness put it "Encode invariants in your data, don't enforce invariants on your data"
[1] https://news.ycombinator.com/item?id=21476261
[2] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
valand · 2020-01-15
Guaranteed almost 100% type soundness!!! Non TypeScript user will hate this.
Config it to maximum strictness!
Add in runtime type checks
Parse, don't validate https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
Bring type to business logic, e.g.
RegisteredUser { userId: number, email: string }
AnonymousUser { userId: number, email: null }
User = RegisteredUser | AnonymousUser
Handle errors ala Rust,
Return errors/errortype rather than throws
Utilize structure to determine result, e.g.
Failure { error: Error, value: null }
Success<T> { error: null, value: T }
It is worth it. My experience with TypeScript is a whole lot different.
Conspiracy theory inducing facts:
- LogRocket's main use is monitoring bugs in browser application
- Airbnb is LogRocket's customer
hjek · 2018-11-03
You can write Haskell in the Racket environment[0].
Racket has a lot of well-designed and well-documented libraries that are a joy to work with. In his Racket manifesto Mathias Felleisen (author of The Little Schemer) describes how Racket is supposed to be useful for programmers at any level.
I'm currently working on a web app using the Datalog[1] back end. I guess that's very similar to Clojure's Datomic or core.logic, and I'm not sure which is "best", but Racket is certainly good.
[0]: https://lexi-lambda.github.io/blog/2017/05/27/realizing-hackett-a-meta…
[1]: https://docs.racket-lang.org/datalog/interop.html
nemoniac · 2017-09-05
Another one for that list is Hackett, a promising language in development that I'm following closely. It too piggybacks on Racket and aims to bring together the best features of Haskell and of Lispiness.
https://lexi-lambda.github.io/blog/2017/08/28/hackett-progress-report-…
hutao · 2026-02-21
Note that the division-by-zero example used in this article is not the best example to demonstrate "Parse, Don't Validate," because it relies on encapsulation. The principle of "Parse, Don't Validate" is best embodied by functions that transform untrusted data into some data type which is correct by construction.
Alexis King, the author of the original "Parse, Don't Validate" article, also published a follow-up, "Names are not type safety" [0] clarifying that the "newtype" pattern (such as hiding a nonzero integer in a wrapper type) provide weaker guarantees than correctness by construction. Her original "Parse, Don't Validate" article also includes the following caveat:
> Use abstract datatypes to make validators “look like” parsers. Sometimes, making an illegal state truly unrepresentable is just plain impractical given the tools Haskell provides, such as ensuring an integer is in a particular range. In that case, use an abstract newtype with a smart constructor to “fake” a parser from a validator.
So, an abstract data type that protects its inner data is really a "validator" that tries to resemble a "parser" in cases where the type system itself cannot encode the invariant.
The article's second example, the non-empty vec, is a better example, because it encodes within the type system the invariant that one element must exist. The crux of Alexis King's article is that programs should be structured so that functions return data types designed to be correct by…
vacuity · 2025-10-14
This approach is brittle to upstream code changes, and I don't think it should be used, but at least there is an effort to avoid catastrophic behavior (unlike Linus's philosophy for Linux). Both panicking and arbitrarily-nested error bubbling are more standard and recommended, but I think they are still suboptimal, mainly for code quality/maintainability, and should be phased out gradually. I think the viable methods for robust software are promptly handling errors or making errors unrepresentable. To promptly handle errors, the program should be structured such that error bubbling nests shallowly. Unrepresentable errors work within type systems or formal verification, such as the classic advice of "parse, don't validate"[0], giving no way to speak of an error state (so hopefully a cosmic ray never causes it!). These latter approaches are more ideal, but can be a pain to implement.
[0] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
kh9sd · 2024-09-04
Very nice article, I liked it a lot! It personally resonated with me and my own conclusion that the core "benefit" of FP is (for lack of a better work, stupid Bitcoin) "proof of work".
Writing functions FP is essentially all about returning results from a function, which is proof that that a computation has occurred. If you don't have that return value, that proof, then obviously the rest of your code can't and shouldn't continue, and FP makes it obvious compared to more traditional imperative approaches.
And this idea extends into so many other things that people consider core, or at least originating from FP. Result/Option types come to mind, making the possible failure of "proof of work" explicit in the type signature, so people are forced to consciously handle it. It also leads into the whole idea of type driven design, one of my favorite articles, "Parse, don’t validate"[1], describes this as well, making types that clearly restrict and set the expectations needed for the "proof of work" to always be returned from a function.
[1] https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
hansvm · 2023-08-17
You make your life substantially easier if all the functions/methods/procedures/blocks/... in your code are "total" -- if they transform every possible input into the output one would expect from the name and type signature. With that as a guiding principle, I tend toward the following strategies, the particular choice of which depends on other details. Most often I'll combine 2/5, throwing a compile-time error for the broken branches, later realizing that the abstraction I chose was stupid, and then writing code that actually does what I want.
1. Inline the work being done. If you've only implemented a single case it doesn't deserve a named function that does something different from what the name indicates. Refactor it later if you ever need when you have more cases.
2. Ensure the error happens at compilation. C# has diagnostic directives. Zig has the @compileError builtin. C/C++ can fail using the preprocessor. It's a relatively common feature, and if you ensure that when your buggy/unfinished function compiles it's correct then that mitigates the harm you might cause (and serves as documentation: @compileError("TODO: ...")).
3. Ensure the type signature captures the problem. If some inputs result in crashes, return an error-code/error-condition/failure-union/optional/nullable/... so that the caller knows shit might happen and can deal with it at runtime if it does.
4. Make it abundantly clear that there's an issue. Name it…
dharmaturtle · 2022-10-02
Zod allows for more complex validation like "input cannot exceed 256 chars", "number must be positive", or even arbitrary "refinements". I could, of course, simply write a function that will `Abort` instead, but tRPC+Zod handles that automatically - zero boilerplate. My current understanding is that I'll need to invoke Abort manually if the Zod.parse fails.
Importantly Zod philosophically follows "parse, don't validate" https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/ which means that once validation is done, you get a type - a parsed value. This means you can validate clientside and serverside using the same types. Again, sure you could do the same with Telefunc, but you still need to invoke Abort manually and every single RPC definition you have needs to be followed by Zod.parse. A small cost to be sure, but what are the advantages of Telefunc over tRPC? I'm not seeing any right now.
tRPC also isn't strongly tied to Zod, it also works with yup, superstruct, and custom validators.
tene · 2022-06-19
> no commonly used statically typed language helps here because they all allow null references
This is false.
TypeScript, Swift, and Rust are commonly used and support non-nullable references.
> those languages, don't help you because you are dealing with real world data where inputs to your system can be null or not so you end up using some type system escape hatch anyway.
You don't need escape hatches to deal with "real world data" that may be missing some values. This blog post is my favorite detailed comparison of handling "real world data" in static vs dynamic languages: https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-…
Rust's serde_json docs on "Operating on untyped JSON values" are also a pretty good description of working with "real world data" where you want to examine an arbitrary document: https://docs.serde.rs/serde_json/#operating-on-untyped-json-values
The only requirement for working with "real world data" in a static non-nullable language is to choose whatever kind of behaviour you want when working with the data. Everything you can do with null references, you can do better with option types; there is nothing that null references uniquely permit.
anyonecancode · 2021-07-02
I think copilot is solving the wrong problem. A future of programming where we're higher up the abstraction tree is absolutely something I want to see. I am taking advantage of that right now -- I'm a decently good programmer, in the sense that I can write useful, robust, reliable software, but I'm pretty high up the stack, working in languages like Java or even higher up the stack that free me from worrying about the fine details of memory allocation or the particular architecture of the hardware my code is running on.
Copilot is NOT a shift up the abstraction tree. Over the last few years, though, I've realized the the concept of typing is. Typed programming is becoming more popular and prominent beyond just traditional "typed" languages -- see TypeScript in JS land, Sorbet in Ruby, type hinting in Python, etc. This is where I can see the future of programming being realized. An expressive type system lets you encode valid data and even valid logic so that the "building blocks" of your program are now bigger and more abstract and reliable. Declarative "parse don't validate"[1] is where we're eventually headed, IMO.
An AI that can help us to both _create_ new, useful types, and then help us _choose_ the best type, would be super helpful. I believe that's beyond the current abilities of AI, but can imagine that in the future. And that would be amazing, as it would then truly be moving us up the abstraction tree in the same way that, for instance, garbage collection has…