5 ms·
I would recommend not calling such things invariants and not thinking about them in the same way you think about invariants. If you would allow a comparison to
by BlackFly 2y ago
I would recommend not calling such things invariants and not thinking about them in the same way you think about invariants. If you would allow a comparison to physics then you would call such things assumptions: frictionless pulleys, small angle deflections, much slower than light speed, less than nuclear density. The physical theory developed is then correct while the assumptions hold. If you are making such an assumption and your language supports it, add a debug assertion if possible.
For me, invariants are constructs of the design of an algorithm. A list doesn't change while iterating over it-by construction, not because someone else owns the reference and promises not to change it concurrently. This structure can only be instantiated via this function which ensures that that string field can always contains only numeric digits. Those are invariants, thing you enforce by design so you can rely on that later. Assumptions instead are things you do not enforce but rely on anyways.
Back to physics, an invariant would generally be something like conservation of energy or the speed of light being universal. Both of these things are only invariant in certain physical theories which enforce them by construction.
- DougBTX 2y agoAgreed, we shouldn’t use “invariant” as a shorthand for “not expected to change”. We can still talk about “implicit assumptions” and “explicit assumptions”, so the article still works after that replacement. The headline isn’t as punchy though.
- richrichie 2y agoWell stated. Physics analogy is spot on.
- n4r9 2y agoPrecisely. If you asked a developer at the time whether the "assumption that any loaned memory would fit into one region" was static, I'm guessing they'd say "no, it's just what we've assumed for now. I'm hoping that no one changes that without it being discussed properly."
- Joel_Mckay 2y agoPersonally, I view most invariants as another form of state embedding. It can often be unrolled or duplicated (GPU mailbox) for parallelization, but does not necessarily exclude out-of-order chip specific atomic operations on semaphores etc. There is a folk song written about Black flies: https://www.youtube.com/watch?v=w2q8jEuH3nk https://www.youtube.com/watch?v=w2q8jEuH3nk =3
- MereInterest 2y agoI think I'd put the difference between "invariant" and "assumption" as purely a difference in framing. Both describe a problem in communication between two components of a system. Suppose there are two components, where the output of component A is sent to component B. Both components seem to be working correctly, but when run together they are not producing the expected output. * Option 1: Component A is violating an invariant of its output type. Whether that is invalid data (e.g. nullptr), internal inconsistency (e.g. an index that will be out of bounds for its array), or some (e.g. matrices with determinant one), the output generated by Component A is wrong in some way. * Option 2: Component B is making an unjustified assumption about its input type. The implementation was only valid when the assumption held, but Component B's interface implied that it could accept a wider range of inputs. The only difference between these two cases is the human-level description of what the type is "supposed" to hold. By describing it as an "invariant", the blame is placed at Component A. By describing it as an "assumption", the blame is placed at Component B.
- im3w1l 2y agoWell consider a system built on the assumption of conservation of energy. Then a few product iterations the system might start leaking energy into the environment. Maybe it's the neodymium magnet in some unrelated component leading to eddy current braking?
- epgui 2y agoThat's not a violation of the conservation of energy, and the invariant isn't broken.
- xmcqdpt2 2y agoI don't think the examples you provided differ all that much. Someone might add a new constructor that no longer sets the string fields correctly, or your function might get modified so that it can be passed a mutable structure. Type systems and tests can be used to make it harder for future people to break your invariants, but they can still be broken.
- BlackFly 2y agoYou've chosen a good example to further this discussion precisely because it further emphasizes the differences between assumptions and invariants in code: the point when it breaks. Assumptions break at unpredictable times (like when the machine architecture changes), invariants break when someone modifies the code. If you or a colleague have modified the constructor NumericString to no longer enforce the invariant that it only includes digits (remember though that utf-8 has more than just the ascii digit characters, we aren't necessarily talking about ASCIINumericString) but can include hyphens, a reviewer should have alarms going off in their head: somewhere in the code surely some conversions to numbers is going to fail without further manipulation. That is the benefit of an invariant, it is a part of the code by construction and enforced there; the purpose of reviews and the purpose of encapsulation and strong typing is to narrow the boundary where the invariant must be enforced. Assumption on the other hand have no narrow boundary. Memory size changes, what part of your code is no longer valid? It is potentially unbounded if your code was allowed to rely on something that was not enforced.
- jayd16 2y agoTheres a difference between the user breaking an assumption and a self inflicted breaking change. For starters the former can happen after compile time.