108

I'm reading Coders at Work, and in it there's a lot of talk about invariants. As far as I've understood it, an invariant is a condition which holds both before and after an expression. They're, among other things, useful in proving that loop is correct, if I remember my Logic course correctly.

Is my description correct, or have I missed something? Have you ever used them in your program? And if so, how did they benefit?

gablin
  • 17,525

7 Answers7

85

In OOP, an invariant is a set of assertions that must always hold true during the life of an object for the program to be valid. It should hold true from the end of the constructor to the start of the destructor whenever the object is not currently executing a method that changes its state.

An example of invariant could be that exactly one of two member variables should be null. Or that if one has a given value, then the set of allowed values for the other is this or that...

I sometime use a member function of the object to check that the invariant holds. If this is not the case, an assert is raised. And the method is called at the start and exit of each method that changes the object (in C++, this is only one line...)

Xavier Nodet
  • 3,754
61

Well, the stuff I'm seeing in this thread is all great, but I have a definition of an 'invariant' that has been tremendously helpful for me at work.

An invariant is any logical rule that must be obeyed throughout the execution of your program that can be communicated to a human, but not to your compiler.

This definition is helpful because it cleaves out conditions into two groups: those the compiler can be trusted with enforcing, and those that must be documented, discussed, commented, or otherwise communicated to contributors in order for them to interact with the codebase without introducing bugs.

Also, this definition is helpful because it allows you to use the generalization, "Invariants are bad".

As an example, the shifter in a manual transmission car is engineered to avoid an invariant. If I wanted, I could build a transmission with one lever for each gear. This lever could be forward ("engaged") or back ("disengaged"). In such a system, I have created an "invariant", which might be documented as such:

"It is critical that the currently engaged gear be disengaged before a different gear is engaged. To engage any two gears at the same time will cause mechanical stress that will tear the transmission apart. Always disengage the currently engaged gear before engaging another."

And so, one might blame broken transmissions on sloppy driving. Modern cars, however, use a single stick that pivots around among the gears. It's designed in such a way that, on a modern stick-shift car, it is not possible to engage two gears at the same time.

In this way, we could say that the transmission has been engineered to 'remove the invariant', because it does not permit itself to be mechanically configured in a way that violates the logical rule.

Every invariant of this kind that you remove from your code is an improvement, because it lowers the cognitive load of working with it.

6

Based on the following quote from Coders At Work...

But once you know the invariant that it's maintaining, you can see, ah, if we maintain that invariant then we'll get log lookup time.

...I guess "invariant" = "condition you want to maintain to ensure a desired effect".

It seems that invariant has two senses that differ in a subtle way:

  1. Something that stays the same.
  2. Something that you're trying to keep the same, in order to achieve goal X (such as a "log lookup time" above).

So 1 is like an assertion; 2 is like a tool for proving correctness, performance, or other properties - I think. See the Wikipedia article for an example of 2 (proving the correctness of the solution to the MU puzzle).

Actually a 3rd sense of invariant is:

.3. What the program (or module or function) is supposed to do; in other words, its purpose.

From the same Coders At Work interview:

But what makes big software manageable is having some global invariants or big-picture statements about what it's supposed to do and what things are supposed to be true.

6

An invariant (in common sense) means some conditions that must be true at some point in time or even always while your program is executing. e.g. PreConditions and PostConditions can be used to assert some conditions that must be true when a function is called and when it returns. Object invariants can be used to assert that a object must have a valid state throughout the time it exists. This is the design by contract principle.
I have used invariants informally using checks in code. But more recently I am playing with the code contracts library for .Net that directly supports invariants.

softveda
  • 2,679
4

An invariant is like a rule or an assumption that can be used to dictate the logic of your program.

For example, suppose you have some software application that keeps track of user accounts. Suppose also that user can have multiple account, but for whatever reason you need to differentiate between a user's main account and "alias" accounts.

This could be a DB record or something else, but for now lets assume each user account is represented by a class object.

class userAccount { private char * pUserName; private char * pParentAccountUserName;

... }

An invariant might be the assumption that if pParentAccountUserName is NULL or empty then this object is the parent account. You can use this invariant to distinguish different types of account. There are probably better methods to distinguish different types of user accounts, so keep in mind this is just an example to show how an invariant might be used.

Pemdas
  • 5,395
  • 3
  • 23
  • 41
3

Coming from a physics background, in physics we have invariants, which are essentially quantities which do not vary throughout an entire computation/simulation. For instance, in physics, for a closed system total energy is conserved. Or again in physics, if two particle collide, the resulting fragments must contain exactly the energy they started with, and exactly the same momentum (a vector quantity). Usually there aren't enough invariants to totally specify the result. For instance in the 2particle collision, we have four invariants, three momentum components, and an energy component, but the system has six degrees of freedom (six numbers to describe its state). The invariants ought to be conserved to within rounding error, but their conservation does not prove the solution is correct.

So typically, these things are important as sanity checks, but by themselves they can't prove correctness.

0

I'd say the whole development process relies on invariants.

You write some code, that produces observable behaviour.

You then modify the code, with the goal that most of the observable behaviour remains invariant. When the invariant fails, you then git diff to figure out what change caused it to break.

Unit tests are used when the required invariant behaviours become too much to check in the development loop.

Learning to code is mostly about figuring out how to do this; i.e. decomposing the desired behaviour into a set of invariant behaviours which you are able to evolve into complex behaviours.

A good example is the movement code for the game Celeste - 4k+ lines just to make it feel good.

source


I've been talking about invariants between runs of the program. But when you hear someone say invariants they probably mean something that holds through out the lifetime of the program.


An invariant is something you can write a predicate for that returns false only when the program is incorrect.


An invariant is something you can write a hash function for such that the value of the hash only changes if the program is incorrect.