Jump to page: 1 2
Thread overview
Broken contract programing
May 13, 2015
iackhtak
May 13, 2015
Timon Gehr
May 13, 2015
Benjamin Thaut
May 13, 2015
Timon Gehr
May 13, 2015
Benjamin Thaut
May 13, 2015
Idan Arye
May 13, 2015
Idan Arye
May 13, 2015
Timon Gehr
May 13, 2015
iackhtak
May 13, 2015
Idan Arye
May 13, 2015
Timon Gehr
May 18, 2015
Marco Leise
May 13, 2015
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:
>> 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
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 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
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
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
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:
>> 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
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
« First   ‹ Prev
1 2