www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Broken contract programing

reply "iackhtak" <iackhtak gmail.com> writes:
There was discussion about broken contract programing. One broken
thing was "in" contract within inheritance. If you add different
"in"-contract in overridden parent and derived function only one
will be checked.
I thought that solution is to ban "in"-contract for derived
function. "In"-contract says what you can pass. If you want to
accept input without any constraints you can't add new constraint
in inherited stuff because your inherited stuff can by used in
any context where base thing can. Within this context any
arguments can be passed. This way adding new constraints on input
brakes Liskov substitution principle and have to be banned.
Theoretically it can be allowed to loose contract(and extend
interface) but the thing looks hard to implement.

Conversely "out" can be narrowed without any restrictions.
May 13 2015
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/13/2015 12:51 PM, iackhtak wrote:
 There was discussion about broken contract programing. One broken
 thing was "in" contract within inheritance. If you add different
 "in"-contract in overridden parent and derived function only one
 will be checked.
No, this is incorrect. Only one needs to pass and if one does not, the other is checked as well. class C{ int foo()in{}out(r){assert(r<=10);}body{ return 3; } } class D: C{ override int foo()in{assert(false);}out(r){assert(r>=10);}body{ return 10; } } void main(){ D d=new D(); d.foo(); } What is the problem? The problem you may have read about is that if no contract is specified for an overriding function, the parent contract is automatically loosened completely: class C{ void foo()in{assert(false);}body{} } class D: C{ override void foo(){} } void main(){ D d=new D(); d.foo(); // ok }
May 13 2015
next sibling parent reply "Benjamin Thaut" <code benjamin-thaut.de> writes:
On Wednesday, 13 May 2015 at 12:05:48 UTC, Timon Gehr wrote:
 On 05/13/2015 12:51 PM, iackhtak wrote:
 There was discussion about broken contract programing. One 
 broken
 thing was "in" contract within inheritance. If you add 
 different
 "in"-contract in overridden parent and derived function only 
 one
 will be checked.
No, this is incorrect. Only one needs to pass and if one does not, the other is checked as well.
Wasn't the point that the set of values the derived contract accepts must be a superset of the set of values the base contract accepts? E.g. class Base { int foo(int x) in { assert( x >= 0; ); body { ... } } This would be ok: class DerivedOk : Base { override int foo(int x) in { assert( x >= 0 || x < -5 ); } body { ... } } This would be broken: class DerivedBroken : Base { override int foo(int x) in { assert( x >= 0 && x < 5 ); } body { ... } } Because if you have a pointer to the base type it must always work with the contract specified in the definition of the base type (because that is most likely the only thing the client sees). So further restricting the possible range of values will possibly break code when swapping out implementations. However increasing the range of possible values will not break when swapping implementations.
May 13 2015
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/13/2015 02:16 PM, Benjamin Thaut wrote:
 On Wednesday, 13 May 2015 at 12:05:48 UTC, Timon Gehr wrote:
 On 05/13/2015 12:51 PM, iackhtak wrote:
 There was discussion about broken contract programing. One broken
 thing was "in" contract within inheritance. If you add different
 "in"-contract in overridden parent and derived function only one
 will be checked.
No, this is incorrect. Only one needs to pass and if one does not, the other is checked as well.
Wasn't the point that the set of values the derived contract accepts must be a superset of the set of values the base contract accepts?
Yes, but this is enforced automatically.
 E.g.

 class Base
 {
     int foo(int x)
     in { assert( x >= 0; );
     body { ... }
 }

 This would be ok:

 class DerivedOk : Base
 {
     override int foo(int x)
     in { assert( x >= 0 || x < -5 ); }
     body { ... }
 }

 This would be broken:

 class DerivedBroken : Base
 {
     override int foo(int x)
     in { assert( x >= 0 && x < 5 ); }
     body { ... }
 }
 ...
This will accept the same arguments as Base. Only one in-contract in the inheritance chain must pass in order for the call to go through.
May 13 2015
parent reply "Benjamin Thaut" <code benjamin-thaut.de> writes:
On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote:
 This will accept the same arguments as Base. Only one 
 in-contract in the inheritance chain must pass in order for the 
 call to go through.
But wasn't exactly that the problem about the current implementation? Shouldn't all 'in' contracts in the chain be checked?
May 13 2015
next sibling parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Wednesday, 13 May 2015 at 14:54:05 UTC, Benjamin Thaut wrote:
 On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote:
 This will accept the same arguments as Base. Only one 
 in-contract in the inheritance chain must pass in order for 
 the call to go through.
But wasn't exactly that the problem about the current implementation? Shouldn't all 'in' contracts in the chain be checked?
The topic is non-trivial. Dart has even deliberately picked an unsound inheritance typing-model because it is more useful. Consider the following scenario: You have a superclass Shelter, then you create subclass DogShelter. The Shelter has a virtual function add_pet(Animal x) . So clearly the DogShelter will have to accept the _type_ Animal in its add_pet specialization because of the type system. That does not mean it is not a bug if you bring a Cat to the DogShelter. It merely means that you cannot catch such bugs with the type system alone. The DogShelter might sit behind another infrastructure that is supposed to prevent non-Dog animals from entering. So it makes sense to not conflate invariants/contracts with the type system.
May 13 2015
parent reply "Idan Arye" <GenericNPC gmail.com> writes:
On Wednesday, 13 May 2015 at 17:47:27 UTC, Ola Fosheim Grøstad 
wrote:
 On Wednesday, 13 May 2015 at 14:54:05 UTC, Benjamin Thaut wrote:
 On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote:
 This will accept the same arguments as Base. Only one 
 in-contract in the inheritance chain must pass in order for 
 the call to go through.
But wasn't exactly that the problem about the current implementation? Shouldn't all 'in' contracts in the chain be checked?
The topic is non-trivial. Dart has even deliberately picked an unsound inheritance typing-model because it is more useful. Consider the following scenario: You have a superclass Shelter, then you create subclass DogShelter. The Shelter has a virtual function add_pet(Animal x) . So clearly the DogShelter will have to accept the _type_ Animal in its add_pet specialization because of the type system. That does not mean it is not a bug if you bring a Cat to the DogShelter. It merely means that you cannot catch such bugs with the type system alone. The DogShelter might sit behind another infrastructure that is supposed to prevent non-Dog animals from entering. So it makes sense to not conflate invariants/contracts with the type system.
http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29
May 13 2015
parent reply "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Wednesday, 13 May 2015 at 18:32:17 UTC, Idan Arye wrote:
 On Wednesday, 13 May 2015 at 17:47:27 UTC, Ola Fosheim Grøstad 
 wrote:
 On Wednesday, 13 May 2015 at 14:54:05 UTC, Benjamin Thaut 
 wrote:
 On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote:
 This will accept the same arguments as Base. Only one 
 in-contract in the inheritance chain must pass in order for 
 the call to go through.
But wasn't exactly that the problem about the current implementation? Shouldn't all 'in' contracts in the chain be checked?
The topic is non-trivial. Dart has even deliberately picked an unsound inheritance typing-model because it is more useful. Consider the following scenario: You have a superclass Shelter, then you create subclass DogShelter. The Shelter has a virtual function add_pet(Animal x) . So clearly the DogShelter will have to accept the _type_ Animal in its add_pet specialization because of the type system. That does not mean it is not a bug if you bring a Cat to the DogShelter. It merely means that you cannot catch such bugs with the type system alone. The DogShelter might sit behind another infrastructure that is supposed to prevent non-Dog animals from entering. So it makes sense to not conflate invariants/contracts with the type system.
http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29
Yes… and your point is?
May 13 2015
parent reply "Idan Arye" <GenericNPC gmail.com> writes:
On Wednesday, 13 May 2015 at 21:45:30 UTC, Ola Fosheim Grøstad 
wrote:
 On Wednesday, 13 May 2015 at 18:32:17 UTC, Idan Arye wrote:
 On Wednesday, 13 May 2015 at 17:47:27 UTC, Ola Fosheim Grøstad 
 wrote:
 On Wednesday, 13 May 2015 at 14:54:05 UTC, Benjamin Thaut 
 wrote:
 On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote:
 This will accept the same arguments as Base. Only one 
 in-contract in the inheritance chain must pass in order for 
 the call to go through.
But wasn't exactly that the problem about the current implementation? Shouldn't all 'in' contracts in the chain be checked?
The topic is non-trivial. Dart has even deliberately picked an unsound inheritance typing-model because it is more useful. Consider the following scenario: You have a superclass Shelter, then you create subclass DogShelter. The Shelter has a virtual function add_pet(Animal x) . So clearly the DogShelter will have to accept the _type_ Animal in its add_pet specialization because of the type system. That does not mean it is not a bug if you bring a Cat to the DogShelter. It merely means that you cannot catch such bugs with the type system alone. The DogShelter might sit behind another infrastructure that is supposed to prevent non-Dog animals from entering. So it makes sense to not conflate invariants/contracts with the type system.
http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29
Yes… and your point is?
That a type system can solve this problem if it supports covariant and contravariant generic/template parameters.
May 13 2015
parent "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Wednesday, 13 May 2015 at 23:49:51 UTC, Idan Arye wrote:
 That a type system can solve this problem if it supports 
 covariant and contravariant generic/template parameters.
Not if someone else provided the super class, and generics simply bypasses the OO-inheritance model. Anyway, the key point was that uses of type systems that are workable (not overly tedious) can only catch the most obvious bugs at compile time. Therefore it is useful to be able to constrain invariants to a point where they ensure correctness, not type-soundness.
May 14 2015
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 05/13/2015 04:54 PM, Benjamin Thaut wrote:
 On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote:
 This will accept the same arguments as Base. Only one in-contract in
 the inheritance chain must pass in order for the call to go through.
But wasn't exactly that the problem about the current implementation?
No, this is done right. (What I meant to say was that one passing in-contract in the inheritance chain is sufficient.)
 Shouldn't all 'in' contracts in the chain be checked?
They are all checked in turn until one is successful.
May 13 2015
prev sibling parent "iackhtak" <iackhtak gmail.com> writes:
On Wednesday, 13 May 2015 at 12:05:48 UTC, Timon Gehr wrote:
 On 05/13/2015 12:51 PM, iackhtak wrote:
 There was discussion about broken contract programing. One 
 broken
 thing was "in" contract within inheritance. If you add 
 different
 "in"-contract in overridden parent and derived function only 
 one
 will be checked.
No, this is incorrect. Only one needs to pass and if one does not, the other is checked as well. class C{ int foo()in{}out(r){assert(r<=10);}body{ return 3; } } class D: C{ override int foo()in{assert(false);}out(r){assert(r>=10);}body{ return 10; } } void main(){ D d=new D(); d.foo(); } What is the problem? The problem you may have read about is that if no contract is specified for an overriding function, the parent contract is automatically loosened completely: class C{ void foo()in{assert(false);}body{} } class D: C{ override void foo(){} } void main(){ D d=new D(); d.foo(); // ok }
So I misunderstood this feature:( But the topic thing still a solution.
May 13 2015
prev sibling parent reply "Idan Arye" <GenericNPC gmail.com> writes:
On Wednesday, 13 May 2015 at 10:51:09 UTC, iackhtak wrote:
 There was discussion about broken contract programing. One 
 broken
 thing was "in" contract within inheritance. If you add different
 "in"-contract in overridden parent and derived function only one
 will be checked.
Not "only one" is checked - they are checked until one passes. If only the last one passes all of them need to be checked.
 I thought that solution is to ban "in"-contract for derived
 function. "In"-contract says what you can pass. If you want to
 accept input without any constraints you can't add new 
 constraint
 in inherited stuff because your inherited stuff can by used in
 any context where base thing can. Within this context any
 arguments can be passed. This way adding new constraints on 
 input
 brakes Liskov substitution principle and have to be banned.
 Theoretically it can be allowed to loose contract(and extend
 interface) but the thing looks hard to implement.
I think the `in` contracts check should be statically dispatched, so that only the contracts relevant to the reference-type you are calling the method from will be checked. Take http://dpaste.dzfl.pl/9ec5962518391 for example. The first time I call `a.foo(15)` it rightfully fails, because `A.foo` only accepts values smaller than 10. When I call `b.foo(15)` it rightfully succeeds, because `B.foo` loosens the contract to accept values smaller than 20. The problem is that the second call to `a.foo(15)` succeeds. `a` contains `B`, but the user should not rely on that! LSP says that we should be able to use `B`s like they were `A` - not `A`s like they were `B`s. The `in` contract should make sure the user of the code is using it correctly, but it's not doing it's job here because it allows us to call `A.foo` with 15, just because our `A` reference happens to point to an instance of `B`.
May 13 2015
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/13/2015 02:16 PM, Idan Arye wrote:

 I think the `in` contracts check should be statically dispatched, so
 that only the contracts relevant to the reference-type you are calling
 the method from will be checked.
https://issues.dlang.org/show_bug.cgi?id=6857
May 13 2015
parent Marco Leise <Marco.Leise gmx.de> writes:
Am Wed, 13 May 2015 14:35:53 +0200
schrieb Timon Gehr <timon.gehr gmx.ch>:

 On 05/13/2015 02:16 PM, Idan Arye wrote:

 I think the `in` contracts check should be statically dispatched, so
 that only the contracts relevant to the reference-type you are calling
 the method from will be checked.
https://issues.dlang.org/show_bug.cgi?id=6857
This discussion over there was quite a thriller. From the first post it looked like a clear case to me: I pass an 'A' into 'foo', so I deal with 'A's API which includes the contracts it defines and what methods are declared. Seemed so logical. Then Walter & Andrei were all like "Guys read up on OOP, you seem not to understand the details! The way we handle contracts is the only possible way!", passing the argument by authority up to Meyer. And finally Meyer himself had to think for a moment before saying the static type should probably be checked for contracts, not the dynamic one and his book wasn't explicit about it either, but it could be deduced. So much energy went into writing endless repetitive comments, that could have been put into trying to understand the issue at hand and revalidating ones views and memories when four or more people hint at a blind spot. Maybe one has to be in the situation where one constantly has to put energy into proving to people who made up a new feature in 5 minutes, why exactly it is not going to work. Anyways the bug is pre-approved and looking for someone to hack on the compiler. -- Marco P.S.: I just noticed I had a misconception about in-contracts, too. Where I thought they would be inherited per method override, it is actually the case that not copying the in-contract states that my derived method will work with any input. So this was all very educational at least.
May 17 2015