digitalmars.D - Broken contract programing
- iackhtak (14/14) May 13 2015 There was discussion about broken contract programing. One broken
- Timon Gehr (31/35) May 13 2015 No, this is incorrect. Only one needs to pass and if one does not, the
- Benjamin Thaut (31/41) May 13 2015 Wasn't the point that the set of values the derived contract
- Timon Gehr (4/38) May 13 2015 This will accept the same arguments as Base. Only one in-contract in the...
- Benjamin Thaut (4/7) May 13 2015 But wasn't exactly that the problem about the current
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (15/22) May 13 2015 The topic is non-trivial. Dart has even deliberately picked an
- Idan Arye (3/26) May 13 2015 http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_s...
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (2/34) May 13 2015 Yes… and your point is?
- Idan Arye (4/39) May 13 2015 That a type system can solve this problem if it supports
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (7/9) May 14 2015 Not if someone else provided the super class, and generics simply
- Timon Gehr (4/9) May 13 2015 No, this is done right. (What I meant to say was that one passing
- iackhtak (3/42) May 13 2015 So I misunderstood this feature:( But the topic thing still a
- Idan Arye (18/34) May 13 2015 Not "only one" is checked - they are checked until one passes. If
- Timon Gehr (2/6) May 13 2015 https://issues.dlang.org/show_bug.cgi?id=6857
- Marco Leise (31/39) May 17 2015 This discussion over there was quite a thriller. From the
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
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
On Wednesday, 13 May 2015 at 12:05:48 UTC, Timon Gehr wrote:On 05/13/2015 12:51 PM, iackhtak wrote: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.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.
May 13 2015
On 05/13/2015 02:16 PM, Benjamin Thaut wrote:On Wednesday, 13 May 2015 at 12:05:48 UTC, Timon Gehr wrote:Yes, but this is enforced automatically.On 05/13/2015 12:51 PM, iackhtak wrote: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?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.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
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
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: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.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
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:http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote: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.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
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:Yes… and your point is?On Wednesday, 13 May 2015 at 14:54:05 UTC, Benjamin Thaut wrote:http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote: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.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
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:That a type system can solve this problem if it supports covariant and contravariant generic/template parameters.On Wednesday, 13 May 2015 at 17:47:27 UTC, Ola Fosheim Grøstad wrote:Yes… and your point is?On Wednesday, 13 May 2015 at 14:54:05 UTC, Benjamin Thaut wrote:http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote: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.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
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
On 05/13/2015 04:54 PM, Benjamin Thaut wrote:On Wednesday, 13 May 2015 at 12:54:52 UTC, Timon Gehr wrote:No, this is done right. (What I meant to say was that one passing in-contract in the inheritance chain is sufficient.)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?They are all checked in turn until one is successful.
May 13 2015
On Wednesday, 13 May 2015 at 12:05:48 UTC, Timon Gehr wrote:On 05/13/2015 12:51 PM, iackhtak wrote:So I misunderstood this feature:( But the topic thing still a solution.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
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
On 05/13/2015 02:16 PM, Idan Arye wrote:https://issues.dlang.org/show_bug.cgi?id=6857I 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.
May 13 2015
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: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.https://issues.dlang.org/show_bug.cgi?id=6857I 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.
May 17 2015