August 22, 2004
This was originally a bug report, but the conversation has moved on...

In article <cg9vvl$fe8$1@digitaldaemon.com>, Bent Rasmussen says...
>
>> I'd like to see a reasoned treatment of the consequences of suspending
>> invariant calls for all but the outermost
>> instance method call. It sounds nice, and I have no immediate counter
>> arguments, but I think it needs some thinking
>> about.
>
>OOSC page 364 and onwards.
>
>"In spite of its name, the invariant does not need to be satisfied at all times..."
>
>"it is perfectly acceptable for a procedure g to begin by trying to work towards its goal -- its postcondition -- and in the process to destroy the invariant (as in human affairs, trying to do something useful may disrupt the established order of things); then it spends the second part of its execution trying to restore the invariant without loosing too much of whatever ground has been gained"
>
>"Qualified calls of the form a.f ...), executed on behalf of a client, are the only ones that must allways start from a state satisfying the invariant and leave a state satisfying the invariant; there is no such rule for unqualified calls of the form f(...), which are not directly executed by clients but only serve as auxiliary tools for carrying out the needs of qualified calls."
>
>Better quotes may be somewhwere, these are just some i dug out. The reasoning is as you can see quite simple. I think the relaxation is justified since its relatively easy for a class to maintain its invariant compared to the same class trying to maintain the invariant of other classes; at least the scope is easier to grasp. But that's just one way of looking at it....


I remembered slightly more about my example. It was similar in principle to this:

#    class A
#    {
#        // The invariant
#        invariant { assert(xx == yy); }
#
#        // Private variables
#        private int xx;
#        private int yy;
#
#        // Getter and setter functions
#        int x() { return xx; }
#        int y() { return yy; }
#        int x(int n) { xx = n; return n; }
#        int y(int n) { yy = n; return n; }
#
#        // The problem
#        void assign42()
#        {
#            x = 42; // invariant broken
#            y = 42; // invariant restored
#        }
#    }

Of course, this is a dumb invariant, but it's just to show the idea. Now, like I said, Walter's suggestion was to do the equivalent of rewriting assign42() as:

#        void assign42()
#        {
#            xx = 42; // invariant broken
#            yy = 42; // invariant restored
#        }

But when your getter/setter functions are more complicated, this is not so desirable. In effect, you have to "manually inline" (that is, write the content in full) these and many other functions. And that's a problem because getter/setter functions are an abstraction - you can /change/ them, without having to change all the code in the class - unless, that is, you are forced to duplicate the code throughout the class just to stop invariant-checks from happening.

Andy Friesen reported that placing member function calls (including property getters) in the invariant causes a stack overflow. But placing member function calls /anywhere where the invariant is broken/ causes problems too. So I would like to see that relaxed from its current overzealous state.

I /like/ design-by-contract. I want to use it more. But if it stops me from using abstractions like properties, or calling member functions, then I'm just as likely to comment out the invariant as to rewrite the class.

Arcane Jill