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 );
}