|
|
|
|
|
by _dain_
1027 days ago
|
|
Are you familiar with the idea of "making illegal states unrepresentable", and "parse, don't validate"? https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va... You brought up the examples of doing the wrong thing with booleans. With a good type system you can cut down on needing to use booleans in the first place for a lot of things. Your isSomething(x) example is a good one: presumably some code after this is implicitly relying on this predicate being true about x. If you forget to do the check, or you invert the check, then that's a bug. But another way to do this is to encode the predicate into the type system, so the compiler makes you get it right. Concretely, supposing x is a string, and you need to check if x is a valid username before invoking username-related code on x. Then you can have a function like: fn as_username(x: String) -> Optional[Username]
A Username is just a type alias for a String, i.e. the runtime representation is the exact same, with no overhead. You put the parsing/validation logic inside that function. Then code expecting a username will take a value of type Username rather than String. If as_username is the only function with Username in the return type, then having a value of type Username is proof that the as_username function was already called at some point previously, and gave its blessing to the underlying string so that the Optional could be unpacked. match as_username(raw_string) {
// compiler forces us to handle both cases, ie we can't forget
// to check validity
case Some(username: Username) {
// code in here can assume we have a proper username
}
case None {
// handle what to do otherwise
}
}
Sure, you have to write the as_username function correctly, there's no getting around that. But you only have to get it right once. |
|
I haven't heard of those concepts/ideas before. Thanks for linking the article to define those concepts. With your example, and the article mentioning that "parsing should take place at the boundaries" (paraphrase), I can see how types (a la ML families) can be defined and composed give internal coherence once an external input has been parsed and hence validated.
Really interesting approach which I haven't considered before!