3 ms·
Static types are really good for describing invariants, I definitely agree. But it does also describe a lot of implicit invariants. The LoginUser you've define
by yen223 3y ago
Static types are really good for describing invariants, I definitely agree.
But it does also describe a lot of implicit invariants. The LoginUser you've defined has the implicit invariant that a LoginUser only has 3 fields. It is very possible for someone to write a function that relies on a LoginUser only ever having 3 fields, which then breaks if you ever extended LoginUser. This is similar to the example that the article mentions at the start, where a thing that someone assumed would always fit in one memory region no longer did, and that caused all kinds of problems.
Those kinds of implicit invariants are the ones that are tricky to make explicit, even in strongly-typed languages.
- saghm 3y ago> But it does also describe a lot of implicit invariants. The LoginUser you've defined has the implicit invariant that a LoginUser only has 3 fields. It is very possible for someone to write a function that relies on a LoginUser only ever having 3 fields, which then breaks if you ever extended LoginUse Rust does actually have a good way of dealing with this specific invariant. If you mark a struct with the `non_exhaustive` attribute[0], trying to construct it with a literal will fail outside of the package that defines it (even if all fields are marked as public), and trying to pattern match it outside the package that defines it is required to use the operator to ignore any missing fields. Not trying to say that it's possible to encode every possible invariant in Rust, but I do find it useful to leverage all of the tools available, and I've found this one super useful (on both structs and enums) despite not being super well known from what I can tell. [0]: https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute https://doc.rust-lang.org/reference/attributes/type_system.h...
- valenterry 3y agoI think that's a wrong assumption on your side. You are assuming that the type describes the "actual thing". But there is no such actual thing really. All we do is model. And here we model a view of the user within a certain context. Totally possible that a different department will model their user in a different way. This isn't even a technical problem, it's a semantical problem. Trying to reuse the same type throughout the whole world is doomed to fail, no matter what techniques you use.
- atoav 3y agoThis is good advice. If you take your models of the world too seriously chances are you are in the process of programming yourself into a corner, as things get more complex. Getting attached to your classes by writing a single superclass of The User™ is a sure way of missing out on the benefits of the typesystem and create coupling where there should be none. Knowing where to couple things and where to decouple things is one of those hard programming issues, but one that might be even more important than naming things.
- atoav 3y agoThis was a top of my head example of how you fan catch many invariants, not meant to be exhaustive perfect code. I was also implying the user type is something you use within your application, not something you expose to other people directly. If your application has to expose internal data it makes often more sense to add a specific interface for it (e.g. an API with versions). If there is code where people rely on your internal types, it is your responsibility to use semantic versioning to communicate breaking changes, while it is the other person's responsibility to assume breaking changes when versions change. If we are talking about someone other relying on your user to have 3 fields that is a collegue: it is a good idea to test your code against the whole codebase. Then you know ahead what will fail and can act accordingly.