Jump to page: 1 2 3
Thread overview
Temporary suspension of disbelief (invariant)
Oct 27, 2010
bearophile
Oct 27, 2010
Walter Bright
Oct 27, 2010
Jonathan M Davis
Oct 27, 2010
Walter Bright
Oct 27, 2010
Ellery Newcomer
Oct 27, 2010
Rainer Deyke
Oct 27, 2010
Walter Bright
Oct 27, 2010
Rainer Deyke
Oct 27, 2010
Walter Bright
Oct 27, 2010
Stanislav Blinov
Oct 27, 2010
bearophile
Oct 27, 2010
Fawzi Mohamed
Oct 27, 2010
Walter Bright
Oct 27, 2010
Fawzi Mohamed
Oct 27, 2010
Walter Bright
Oct 27, 2010
Fawzi Mohamed
Oct 27, 2010
Walter Bright
Oct 27, 2010
Fawzi Mohamed
Oct 27, 2010
Walter Bright
Oct 27, 2010
Jonathan M Davis
Oct 27, 2010
Fawzi Mohamed
Oct 27, 2010
Stanislav Blinov
October 27, 2010
I have not asked this in the D.learn newsgroup because I think it may be a bit interesting for other general people too.

In D contract programming the invariant is not called before/after private methods. I think that in some cases you may want to disable the invariant even in some public methods. If your class/struct has a public method that invalidates the state of the class instance, and one public method that fixes the class instance state (I am thinking about certain data structures where in a first phase you may add many items, and then you ask the data structure to clean up itself and become coherent. This may avoid some computations), I have found this way to implement it:



import std.stdio: writeln;

class Foo {
    private int someInstanceState = 1;

    // A ghost field, but it does't vanish in release mode
    //   as it is supposed to do:
    // http://d.puremagic.com/issues/show_bug.cgi?id=5027
    private int validState_ghostField = 0;

    private bool isInvariantEnabled() {
        return validState_ghostField >= 0;
    }

    private void incValidState()
        in {
            assert(validState_ghostField < validState_ghostField.max);
        } body {
            validState_ghostField++;
        }

    private void decValidState()
        in {
            assert(validState_ghostField > validState_ghostField.min);
        } body {
            validState_ghostField--;
        }

    invariant() {
        writeln("invariant validState_ghostField: ", validState_ghostField);
        if (isInvariantEnabled()) {
            writeln("Invariant is enabled");
            // tests the coherence of the instance state
            assert(someInstanceState == 1);
        } else {
            writeln("Invariant is disabled");
        }

    }

    public void fixState() {
        scope(exit) incValidState(); // scope(exit)?
        writeln("fixState validState_ghostField: ", validState_ghostField);
        someInstanceState = 1;
    }

    public void invalidateState() {
        scope(exit) decValidState(); // scope(exit)?
        writeln("invalidateState validState_ghostField: ", validState_ghostField);
        someInstanceState = 0;
    }
}

void main() {
    auto f = new Foo;
    writeln("State 1: ", f.someInstanceState);
    writeln("isInvariantEnabled: ", f.isInvariantEnabled());
    f.invalidateState();
    writeln("State 2: ", f.someInstanceState);
    writeln("isInvariantEnabled: ", f.isInvariantEnabled());
    f.fixState();
    writeln("State 3: ", f.someInstanceState);
    writeln("isInvariantEnabled: ", f.isInvariantEnabled());
}


(Like the GC disable/enable validState_ghostField goes up and down, and allows nesting too.)

All this looks bug-prone, and surely hairy, but it looks potentially useful. Is it a good idea to design a class that uses such temporary suspension of the invariant?

Bye,
bearophile
October 27, 2010
bearophile wrote:
> All this looks bug-prone, and surely hairy, but it looks potentially useful.
> Is it a good idea to design a class that uses such temporary suspension of
> the invariant?

An invariant that is not invariant is a meaningless attribute. It's like "logical constness" where classes claim to be const but aren't.
October 27, 2010
On Tuesday, October 26, 2010 17:25:20 Walter Bright wrote:
> bearophile wrote:
> > All this looks bug-prone, and surely hairy, but it looks potentially useful. Is it a good idea to design a class that uses such temporary suspension of the invariant?
> 
> An invariant that is not invariant is a meaningless attribute. It's like "logical constness" where classes claim to be const but aren't.

The only case that I'm aware of where it makes sense for a public function to not run the invariant is for opAssign() to not run it before it itself is run ( http://d.puremagic.com/issues/show_bug.cgi?id=5058 ), but that's because opAssign() is replacing the state of the object rather than updating it, so you don't care what it's state was beforehand. That's a special case though.

In the general case, it would overly complicate things to try and make an invariant not really be an invariant. If you really wanted to do that, you could use class variables which kept the state and made it so that the asserts in the invariant weren't run after a particular function call until another particular function call were made. But that strikes me as a colossally bad idea. Still, if you wanted such functionality, you _can_ get it, so I see no reason to complicate invariants just to make it easier to write what is likely bad code (or at least which is at a higher risk of being bad code).

- Jonathan M Davis
October 27, 2010
On 10/26/2010 07:25 PM, Walter Bright wrote:
>
> An invariant that is not invariant is a meaningless attribute. It's like
> "logical constness" where classes claim to be const but aren't.

an invariant which isn't used because it is too strict isn't much better.

Example:

class with some sort of state and public property function which grabs a bit out of some field or something

inside method, state violates invariant and I can't call this property function

I needed to use the property function, but I also needed the invariant so I shoehorned it into a regular function and explicitly made the calls. Ugly.

Though I suppose it was a slightly different case than what bearophile is proposing
October 27, 2010
On 10/26/2010 18:00, bearophile wrote:
> All this looks bug-prone, and surely hairy, but it looks potentially useful. Is it a good idea to design a class that uses such temporary suspension of the invariant?

I think invariants should be checked whenever a public member function is called from outside the object.  If setting the object to an invalid state from outside is valid, then clearly the state isn't really invalid.

On the other hand, if the object itself calls it own public member functions, then no invariants should be checked.  Not being able to call public member functions while the object is temporarily in an invalid state is too strict.  This is a problem that I actually ran into while using D, and one of the reasons for why I stopped using invariants.


-- 
Rainer Deyke - rainerd@eldwood.com
October 27, 2010
Jonathan M Davis wrote:
> In the general case, it would overly complicate things to try and make an invariant not really be an invariant. If you really wanted to do that, you could use class variables which kept the state and made it so that the asserts in the invariant weren't run after a particular function call until another particular function call were made. But that strikes me as a colossally bad idea. Still, if you wanted such functionality, you _can_ get it, so I see no reason to complicate invariants just to make it easier to write what is likely bad code (or at least which is at a higher risk of being bad code).

Exactly. Having a class invariant that cheats is like painting over rust. It might look new, but the rust will shortly break through.
October 27, 2010
Rainer Deyke wrote:
> On the other hand, if the object itself calls it own public member
> functions, then no invariants should be checked.  Not being able to call
> public member functions while the object is temporarily in an invalid
> state is too strict.  This is a problem that I actually ran into while
> using D, and one of the reasons for why I stopped using invariants.

A solution is to redesign what the class considers public and private. A public member can be a shell around a private implementation, and other class members can call that private implementation without invoking the invariant.
October 27, 2010
On 10/26/2010 20:16, Walter Bright wrote:
> Rainer Deyke wrote:
>> On the other hand, if the object itself calls it own public member functions, then no invariants should be checked.  Not being able to call public member functions while the object is temporarily in an invalid state is too strict.  This is a problem that I actually ran into while using D, and one of the reasons for why I stopped using invariants.
> 
> A solution is to redesign what the class considers public and private. A public member can be a shell around a private implementation, and other class members can call that private implementation without invoking the invariant.

Writing wrapper functions is a waste of my time.  Auto-generating wrapper functions through some sort of meta-programming magic is still a waste of my time, since the process cannot be completely automated.


-- 
Rainer Deyke - rainerd@eldwood.com
October 27, 2010
Rainer Deyke wrote:
> On 10/26/2010 20:16, Walter Bright wrote:
>> Rainer Deyke wrote:
>>> On the other hand, if the object itself calls it own public member
>>> functions, then no invariants should be checked.  Not being able to call
>>> public member functions while the object is temporarily in an invalid
>>> state is too strict.  This is a problem that I actually ran into while
>>> using D, and one of the reasons for why I stopped using invariants.
>> A solution is to redesign what the class considers public and private. A
>> public member can be a shell around a private implementation, and other
>> class members can call that private implementation without invoking the
>> invariant.
> 
> Writing wrapper functions is a waste of my time.  Auto-generating
> wrapper functions through some sort of meta-programming magic is still a
> waste of my time, since the process cannot be completely automated.

Then I agree that not using invariants at all is probably your best option.
October 27, 2010
 27.10.2010 4:00, bearophile wrote:
> I have not asked this in the D.learn newsgroup because I think it may be a bit interesting for other general people too.
>
> In D contract programming the invariant is not called before/after private methods. I think that in some cases you may want to disable the invariant even in some public methods. If your class/struct has a public method that invalidates the state of the class instance, and one public method that fixes the class instance state (I am thinking about certain data structures where in a first phase you may add many items, and then you ask the data structure to clean up itself and become coherent. This may avoid some computations), I have found this way to implement it:
If I get this right, then it is by design that your class may have several general logical states: e.g. "initializing" and "coherent". Given this, I don't see why you'd want to disable invariant checks rather than modify those checks instead to validate current logical state. In fact, that "ghost" field simply serves as a flag for invariant showing which logical state it should enforce. The fact that states are 'logical', e.g. are different while represented by the same physical fields doesn't always rule them out as, uh, class states: you could as well have two separate inner classes that perform initialization and polishing, each with its own invariant. Then you could use those inner classes' private methods (without causing their invariant checks), but in main class' invariant perform an assert on them to ensure their state is valid.

IMHO, proper invariants should be strict and act by the numbers at all times, otherwise there's little to no gain in using them at all.
« First   ‹ Prev
1 2 3