I prefer a slightly more general rule: Make Illegal States Unrepresentable. The "Parse, don't validate" rule is a special case of MISU.
What's the difference? MISU applies even when there's no parsing-like transformation happening. For example, if you have a variable that represents the current state of a network connection, and let's say it can be Disconnected, or Connected to some IP address (this is an oversimplification).
Then one way to do it would be
struct {
connected: bool,
peer_ip: int32
}
The trouble is that this allows us to represent an illegal/meaningless state: we're disconnected but there's still some junk old peer_ip hanging in there. Even worse, we might have written
Now we could have connected = true but peer_ip = None.
The solution is to use a sum type:
type connection =
Disconnected
| Connected of int32
(sorry for using made-up syntax; I hope it's clear to anyone familiar with Rust.)
"Make Illegal States Unrepresentable" applies throughout your program, at every interface between modules or functions in the program, including but not limited to parsing input.
But you can't hold both a `Connection<Connected>` and a `Connection<Disconnected>` in the same variable or pass them to the same function, which defeats the point.
It's the typestate pattern. The compiler enforces valid state transitions and so you can't just pass to the same function. It's useful to ensure that only some transitions are allowed. To be honest, the connection state presented by GP is a bad example. But you shouldn't say "it defeats the point".
A better example is an HTTP writer. One state is `Headers` with the methods `add_header()` and `body()` which returns the response in state `Body`. This enforces that one can't add headers after `body()` has been called. But you can finish the response in the `Body` state. If you do something not in this order, you get a compile error.
Thanks. This had me thinking to myself some. I found this <a href="https://cliffle.com/blog/rust-typestate/" rel="nofollow">https://cliffle.com/blog/rust-typestate/ too, which others may find useful..
articulatepang · · focus · HN ↗
What's the difference? MISU applies even when there's no parsing-like transformation happening. For example, if you have a variable that represents the current state of a network connection, and let's say it can be Disconnected, or Connected to some IP address (this is an oversimplification).
Then one way to do it would be
The trouble is that this allows us to represent an illegal/meaningless state: we're disconnected but there's still some junk old peer_ip hanging in there. Even worse, we might have written Now we could have connected = true but peer_ip = None.The solution is to use a sum type:
(sorry for using made-up syntax; I hope it's clear to anyone familiar with Rust.)"Make Illegal States Unrepresentable" applies throughout your program, at every interface between modules or functions in the program, including but not limited to parsing input.
laszlokorte · · focus · HN ↗
slopinthebag · · focus · HN ↗
bobbylarrybobby · · focus · HN ↗
_nalply · · focus · HN ↗
A better example is an HTTP writer. One state is `Headers` with the methods `add_header()` and `body()` which returns the response in state `Body`. This enforces that one can't add headers after `body()` has been called. But you can finish the response in the `Body` state. If you do something not in this order, you get a compile error.
explodes · · focus · HN ↗