On Wednesday, 12 October 2016 at 08:36:43 UTC, Jonathan M Davis wrote:
That's exactly what it does. Having no contract is considered to be equivalent to having an empty contract. So, because an empty contract will never fail, not having an in contract in the base class (or interface) function is equivalent to not having any in contracts anywhere in the inheritance chain, which is pretty annoying. The ||ing and &&ing done with in and out contracts and inheritance is critical to having contracts work correctly with inheritance. And in contracts work properly when each level declares one, but it probably would be better if having an in contract were illegal if the base class didn't have one, since then you'd at least avoid accidentally having an in contract that's pointless. out contracts shouldn't have any of these problems though, since they're &&ed, and &&ing with true doesn't change anything. Missing in contracts definitely is an easy way to introduce bugs into your contracts though.

- Jonathan M Davis


OK I'm somewhat struggling with this concept.

As I see it the "in" contract can only place a limit on what values are valid to come in to the method. I could maybe see some questionable argument to make any contract for values coming into the method a subset of the previous valid values, but what you are describing is the opposite: that each contract has to be a superset of all the previous contracts for valid values!

This seems limiting at best, but worse completely at odds with the concept of inheritance building from the most general to the most specific case.

Say I have worker:

    import std.exception;

    class Worker {
    private:
        uint _wage = 10_000;

    public:
        @property uint wage() { return _wage; }
        @property void wage(uint wage)
        in {
            enforce(wage >= 10_000 && wage <= 1_000_000_000);
        } body {
            _wage = wage;
        }
    }

    class WageSlave : Worker {
    private:
        uint _wage = 10_000;

    public:
        override @property uint wage() { return _wage; }
        override @property void wage(uint wage)
        in {
            enforce(wage >= 10_000 && wage <= 40_000);
        } body {
            _wage = wage;
        }
    }

    class CEO : Worker {
    private:
        uint _wage = 1_000_000;

    public:
        override @property uint wage() { return _wage; }
        override @property void wage(uint wage)
        in {
            enforce(wage >= 1_000_000 && wage <= 1_000_000_000);
        } body {
            _wage = wage;
        }
    }

    void main() {
        auto worker = new Worker;
        assertThrown( worker.wage = 9_999 );
        assertThrown( worker.wage = 1_000_000_001 );
        assertNotThrown( worker.wage = 10_000 );
        assertNotThrown( worker.wage = 1_000_000_000 );

        auto slave = new WageSlave;
        assertThrown( slave.wage = 9_999 );
        assertThrown( slave.wage = 1_000_000_001 );
        assertNotThrown( slave.wage = 10_000 );
assertNotThrown( slave.wage = 1_000_000_000 ); // BAD - no throw

        auto ceo = new CEO;
        assertThrown( ceo.wage = 9_999 );
        assertThrown( ceo.wage = 1_000_000_001 );
        assertNotThrown( ceo.wage = 10_000 ); // BAD - no throw
        assertNotThrown( ceo.wage = 1_000_000_000 );
    }


Reply via email to