Parse, Don't Validate and Type-Driven Design in Rust
31–40 of 89 posts
Re: Parse, Don't Validate and Type-Driven Design in Rust
#32Note 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, "Na…
For full-on parse-don't-validate, you essentially need a dependent type system. As a more light-weight partial solution, Rust has been prototyping pattern types, which are types constrained by patterns. For instance a range-restricted integer type could be simply spelled `i8 is 0..100`, or a nonempty slice as `[T] is [_, ..]`. Such a feature would certainly make correctness-by-construction easier in many cases.
The non-empty list implemented as a (T, Vec) is, btw, a nice example of the clash between practicality and theoretical purity. It can't offer you a slice (consecutive view) of its elements without storing the first element twice (which requires immutability and that T: Clone, unlike normal Vec), which makes it fairly useless as a vector. It's okay if you consider it just an abstract list with a more restricted interface.
Re: Parse, Don't Validate and Type-Driven Design in Rust
#33I would just (as a default; the situation varies)... validate prior to the division and handle as appropriate.
The analogous situation I encounter frequently is indexing, e.g. checking if the index is out of bounds. Similar idea; check; print or display an error, then fail that computation without crashing the program. Usually an indication of some bug, which can be tracked down. Or, if it's an array frequently indexed, use a (Canonical for Rust's core) `get` method on the whatever struct owns the array. It returns an Option.
I do think either the article's approach, or validating is better than runtime crashes! There are many patterns in programming. Using Types in this way is something I see a lot of in OSS rust, but it is not my cup of tea. Not heinous in this case, but I think not worth it.
This is the key to this article's philosophy, near the bottom:
> I love creating more types. Five million types for everyone please.
Re: Parse, Don't Validate and Type-Driven Design in Rust
#34Note 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, "Na…
[0] https://geeklaunch.io/blog/make-invalid-states-unrepresentab...
Re: Parse, Don't Validate and Type-Driven Design in Rust
#35Note 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, "Na…
You can also search for "make invalid states impossible/unrepresentable" [0] to find more info on related practices. See "domain modeling made functional" [0] as a nice example [0] https://geeklaunch.io/blog/make-invalid-states-unrepresentab... [1] https://www.youtube.com/watch?v=2JB1_e5wZmU
EDIT: Parent comment was edited to amend the "impossible/unrepresentable" wording
Re: Parse, Don't Validate and Type-Driven Design in Rust
#36Earlier quoted context omitted.
Would have to be F32, no? I cannot think of any way to enforce "non-zero-ness" of the result without making it return an optional Result , and at that point we are basically back to square one...
> Would have to be F32, no? Generally yes. `NonZeroU32::saturating_add(self, other: u32)` is able to return `NonZeroU32` though! ( https://doc.rust-lang.org/std/num/type.NonZeroU32.html#metho... ) > I cannot think of any way to enforce "non-zero-ness" of the result without making it return an optional Result , and at that point we are basically back to square one... `NonZeroU32::checked_add(self, other: u32)` basical…
I was confused at first how that could work, but then I realized that of course, with _unsigned_ integers this works fine because you cannot add a negative number...
Re: Parse, Don't Validate and Type-Driven Design in Rust
#37Earlier quoted context omitted.
You can also search for "make invalid states impossible/unrepresentable" [0] to find more info on related practices. See "domain modeling made functional" [0] as a nice example [0] https://geeklaunch.io/blog/make-invalid-states-unrepresentab... [1] https://www.youtube.com/watch?v=2JB1_e5wZmU
The phrasing that I hear more often is "make illegal states unrepresentable"; both the submitted article and Alexis King's original article use this phrase. At least according to https://fsharpforfunandprofit.com/posts/designing-with-types... , it originates from Yaron Minsky (a programmer at Jane Street who is prominent in the OCaml community). EDIT: Parent comment was edited to amend the "impossible/unrepresentable…
Re: Parse, Don't Validate and Type-Driven Design in Rust
#38The alternative is one type, with many functions that can operate on that type. Like how clojure basically uses maps everywhere and the whole standard library allows you to manipulate them in various ways. The main problem with the many type approach is several same it worse similar types, all incompatible.
Start with a more dynamic type, do stuff that doesn't care about the shape, parse into a more precise type, do stuff that relies on the additional invariants, drop back into the more dynamic type again.
Re: Parse, Don't Validate and Type-Driven Design in Rust
#39The alternative is one type, with many functions that can operate on that type. Like how clojure basically uses maps everywhere and the whole standard library allows you to manipulate them in various ways. The main problem with the many type approach is several same it worse similar types, all incompatible.
Re: Parse, Don't Validate and Type-Driven Design in Rust
#40If we take their "parse" approach, then the types of the arguments a, b, and c have to somehow encode the constraint `b^2 - 4ac >= 0`. This would be a total mess--I can't think of any clean way to do this in Rust. It makes _much_ more sense to simply return an Option and do the validation within the function.
In general, I think validation is often the best way to solve the problem. The only counterexample, which the author fixates on in the post, is when one particular value is constrained in a clean, statically verifiable way. Most of the time, validation is used to check (possibly complex) interactions between multiple values, and "parsing" isn't at all convenient.