www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Static versus dynamic binding of in contracts again - trying to set

reply Stewart Gordon <smjg_1998 yahoo.com> writes:
http://d.puremagic.com/issues/show_bug.cgi?id=6857

It seems we can't all be agreed on how it should be.


It seems part of the problem is that there are two views on the essence of an
in contract 
in an OOP context:

(a) it is part of the API of the class and, as such, a specification to which
any user of 
the class must conform

(b) it is a means of verifying that the target object can handle the input
being passed 
into it

Current D takes view (b), by checking the in contract according to the actual
class of the 
object - dynamic binding.  Several of us (myself included) have argued on the
basis of 
view (a), that it should be checked according to the compile-time type of the
object 
reference - static binding.


So far, there have been arguments both ways.

Arguments in favour of static binding:

s1. A user of a class A, in the general case, doesn't know whether something of
type A is 
a plain A or something of some subclass of A.  This is part of the OO principle
of 
encapsulation.  As such, it doesn't make sense for the class user to rely on
the widened 
preconditions in subclasses it doesn't know about.  (But see d3.)

s2. As such, it helps detect more bugs in code that uses a class of which
programmers are 
likely to create subclasses.

s3. Obeys the Liskov substitution principle.

s4. Consistency with the inability of a base class user to call methods that
exist only in 
a derived class.  If a subclass introduces a new method, that new method
doesn't exist 
from the base class's point of view.  In the same way, if a subclass introduces
new legal 
arguments to a method, those new legal arguments don't exist from the base
class's point 
of view.


Arguments in favour of dynamic binding:

d1. It's part of how overriding is meant to work, per OO principles.  A method
call is 
resolved through the virtual method table of the object's actual class.  (But
this is 
based on the premise that an in contract is just another method.  Which it
isn't.  An in 
contract is a specification of what is a legal call of a method, quite a
different concept 
from the method itself, which provides functionality to the class.)

d2. Bertrand Meyer's book explains why this is the way in which it must work. 
(But the 
truth of this claim has been challenged.)

d3. It allows inputs not allowed by the base class contract to be passed in
under 
controlled conditions - conditions in which some other method is defined from
whose return 
value the legality of some input can be determined.  Example provided by Walter
himself:

     class A {
         void foo(int x) in { assert(x > 0); } body {}
         int bar() { return 1; }
     }

     class B : A {
         void foo(int x) in { assert(x > -2); } body {}
         int bar() { return -1; }
     }

     void fizzbuzz(A a) {
         a.foo(bar());
     }

(While I can see this being useful, it doesn't change the fact that fizzbuzz is
asking the 
class A, not the class B, for the method foo.  And the A.foo contract could
just as well 
have been written
     in { assert(x > 0 || x == bar()); }
thereby causing the call to conform according to view (a) above, hence (d4
aside) enabling 
static binding to be considered without this getting in the way.)

d4. Now that D behaves in this way, there is going to be code out there that
does 
something like the example in d3.  Changing to static binding would break this
code. (This 
seems the one argument for dynamic binding that I'm inclined to agree with.)


deadalnix has pointed out that design of OOP doesn't say anything about
contracts.  In 
which case any claim that the whole OOP paradigm takes either view (a) or view
(b) is 
nonsense.

Maybe what's needed is a clarification in the spec of which concept of an in
contract 
(view (a), view (b) or something else) D intends to implement.

If (a), then we need static binding.  But how do we deal with the code breakage
pointed 
out in d4?

If (b), then we need to stick with the current dynamic binding.  And people
taking view 
(a) will still complain about it.  But a clear spec on the issue would at least
be 
something to which people complaining about (a) can be pointed.

Stewart.
May 05 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 05/05/2012 14:46, Stewart Gordon a écrit :
 d2. Bertrand Meyer's book explains why this is the way in which it must
 work. (But the truth of this claim has been challenged.)
http://se.ethz.ch/~meyer/publications/computer/contract.pdf The extract of the book covering that specific point is available here. Meyer explain HOW thing work and then how this behavior provide some benefices. Careful reading of the passage show that BOTH behavior we are talking here provide such benefices. This reading isn't going to conclude the discussion. Additionally, Meyer state that the in contract is a constraint for the caller. This is an argument in favor of view (a). I do think EIFFEL implementation provide less benefices than the proposed new behavior. I plan to contact Meyer himself to discuss the subject.
 d3. It allows inputs not allowed by the base class contract to be passed
 in under controlled conditions - conditions in which some other method
 is defined from whose return value the legality of some input can be
 determined. Example provided by Walter himself:

 class A {
 void foo(int x) in { assert(x > 0); } body {}
 int bar() { return 1; }
 }

 class B : A {
 void foo(int x) in { assert(x > -2); } body {}
 int bar() { return -1; }
 }

 void fizzbuzz(A a) {
 a.foo(bar());
 }
The fizzbuzz function here is flawed. bar doesn't provide any guarantee on its return value. foo expect fizzbuzz to provide a parameter with certain properties. fizzbuzz is unable to ensure it is doing its part of the contract. Correct alternative are either : - adding an out contract on A.bar to ensure that the value returned is above 0 and so can be safely passed to foo. This solution make B.bar invalid, but fizzbuzz now is able to honor its part f the contract. - adding a new condition to A.foo's in contract to accept bar's return value. This also require that A.bar is made pure. - fizzbuzz is rewritten to ensure that the value returned by bar is valid according to foo's contract. The only contract that fizzbuzz knows about is A.foo and so it should apply criteria on that one. It means that B.bar's return valus will be discarded by fizzbuzz and it will not call a.foo in case of a being an instance of B.
 d4. Now that D behaves in this way, there is going to be code out there
 that does something like the example in d3. Changing to static binding
 would break this code. (This seems the one argument for dynamic binding
 that I'm inclined to agree with.)
As seen above, cases where things risk to break is exactly what we want. Contract is supposed to break bad code ASAP.
 deadalnix has pointed out that design of OOP doesn't say anything about
 contracts. In which case any claim that the whole OOP paradigm takes
 either view (a) or view (b) is nonsense.
After some thinking, I want to make a stronger point. A good guideline in OOP is to hide implementation. A contract is an agreement between the caller and the callee, and so, mustn't be hidden in any way. It certainly exclude the contract as being part of the implementation. view (b) imply that the in contract is part of the implementation (ie, can the provided implementation handle that input or not). OOP don't say anything about contracts, but view (b) is breaking encapsulation, and encapsulation is an OOP principle.
 Maybe what's needed is a clarification in the spec of which concept of
 an in contract (view (a), view (b) or something else) D intends to
 implement.
The spec is very explicit on that point. view (b) is the specified one. But I think it is an error.
 If (a), then we need static binding. But how do we deal with the code
 breakage pointed out in d4?
Breaking flawed code is a benefit, not an issue.
May 05 2012
parent Stewart Gordon <smjg_1998 yahoo.com> writes:
On 05/05/2012 15:07, deadalnix wrote:
<snip>
 http://se.ethz.ch/~meyer/publications/computer/contract.pdf

 The extract of the book covering that specific point is available
 here.  Meyer explain HOW thing work and then how this behavior
 provide some benefices.  Careful reading of the passage show that
 BOTH behavior we are talking here provide such benefices.  This
 reading isn't going to conclude the discussion.
Exactly. Moreover, I noticed that you quoted portions of it on BZ, including "Yet, because of dynamic binding, A may subcontract the execution of r to B, and it is B’s contract that will be applied." There seems to be an unfounded assumption here: that a compiler will necessarily implement contract checking as part of the method body. Really, there's no explanation of "it is B’s contract that will be applied" there. Contract checking happens before and is conceptually separate from method execution.
 Additionally, Meyer state that the in contract is a constraint for
 the caller.  This is an argument in favor of view (a).
That is indeed what it seems to be saying. Among other things: "A precondition violation indicates a bug in the client (caller). The caller did not observe the conditions imposed on correct calls." <snip>
 The fizzbuzz function here is flawed.  bar doesn't provide any
 guarantee on its return value.  foo expect fizzbuzz to provide a
 parameter with certain properties.
Taken the words out of my mouth there. fizzbuzz is not using the class B. It is using the class A. Going by view (a), since foo(-1) is not a legal call according to A's API - and moreover, A's API makes no guarantees that foo(bar()) generally will be a legal call - the code is incorrect. I agree with the rest of your points as well. <snip>
 Maybe what's needed is a clarification in the spec of which
 concept of an in contract (view (a), view (b) or something else) D
 intends to implement.
The spec is very explicit on that point. view (b) is the specified one. But I think it is an error.
<snip> Where is it addressed? Stewart.
May 05 2012
prev sibling parent reply "Regan Heath" <regan netmail.co.nz> writes:
Reading that summary, and deadalnix's further comment I am inclined to  
agree that contracts should be (a) static.

It also made me think.. are we defining in/out contracts in the wrong  
place?  or in the wrong way?  What if we defined them on interfaces  
instead of classes?  After all, interfaces are the other form of contracts  
that we use in OOP and it would make sense to me that an in/out contract  
defined on an interface would be static, and would not change dynamically  
with sub-classes etc.

Perhaps we could have both static and dynamic contracts?  Contracts  
defined on classes (not interfaces) could remain dynamic as they are  
currently, but contracts defined on interfaces could be static.  Giving us  
the best of both worlds.

Regan

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/
May 09 2012
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
On 09/05/2012 11:55, Regan Heath wrote:
 Reading that summary, and deadalnix's further comment I am inclined to agree
that
 contracts should be (a) static.

 It also made me think.. are we defining in/out contracts in the wrong place?
or in the
 wrong way? What if we defined them on interfaces instead of classes?
We should be able to define contracts on interface methods, abstract class methods and normal class methods on an equal footing. http://d.puremagic.com/issues/show_bug.cgi?id=6549 <snip>
 Perhaps we could have both static and dynamic contracts? Contracts defined on
classes (not
 interfaces) could remain dynamic as they are currently, but contracts defined
on
 interfaces could be static. Giving us the best of both worlds.
I'm not sure what the point of this would be. What is the best of the dynamic contract world? Moreover, classes define an API in the same way as interfaces do. Violating a contract on a class method is violating the API in the same way as it is for interfaces. It would be arbitrary and confusing to have this conjured-up difference between classes and interfaces. Stewart.
May 09 2012
parent "Regan Heath" <regan netmail.co.nz> writes:
On Wed, 09 May 2012 13:23:03 +0100, Stewart Gordon <smjg_1998 yahoo.com>  
wrote:

 On 09/05/2012 11:55, Regan Heath wrote:
 Reading that summary, and deadalnix's further comment I am inclined to  
 agree that
 contracts should be (a) static.

 It also made me think.. are we defining in/out contracts in the wrong  
 place? or in the
 wrong way? What if we defined them on interfaces instead of classes?
We should be able to define contracts on interface methods, abstract class methods and normal class methods on an equal footing. http://d.puremagic.com/issues/show_bug.cgi?id=6549 <snip>
 Perhaps we could have both static and dynamic contracts? Contracts  
 defined on classes (not
 interfaces) could remain dynamic as they are currently, but contracts  
 defined on
 interfaces could be static. Giving us the best of both worlds.
I'm not sure what the point of this would be. What is the best of the dynamic contract world?
I've only read the summary, so... If there is no consensus then someone obviously sees some benefit to it. Personally, as I said, I think "static" is the 'correct' way for contracts to work, but perhaps the reason why ppl like dynamic is that 'correct' is not always 'practical' or 'useful'. Having dynamic contracts allows sub-classes to alter them, that could be a benefit to some. It doesn't seem 'correct' to me, violating the substitution principle and all, but I can see how it might be practical/useful.
 Moreover, classes define an API in the same way as interfaces do.
Yes, but an API is only one part or one form of contract. in/out contracts provide additional limitations/guarantees, beyond what a "standard"(*) OOP API does. For example, a "standard"(*) OOP API can only define the type of input, not the range of acceptable values. (*) I'm saying "standard" here because I'm referring to the common usage of class, sub-class, interface without the addition of more complex template, static if, mixin, or other language features not common to the
 Violating a contract on a class method is violating the API in the same  
 way as it is for interfaces.  It would be arbitrary and confusing to  
 have this conjured-up difference between classes and interfaces.
I agree, classes and interfaces define APIs. But, an interface is generally thought of as a more formal API, somehow /more/ of a contract than whatever methods happen to exist in a class at a given time. in/out are currently used as an additional contracts, and currently applied to class methods. But, is that 'correct'? It seemed to me it might be cleaner/clearer and more correct to require in/out to be applied only to interfaces, where there is no concrete implementation. Having applied them to classes and moreover to specific implementations of methods in classes we've created the idea that they could be dynamic and I suspect the current dynamic implementation in DMD just followed naturally from that. I agree, having different behaviour for interfaces/classes /might/ be confusing.. Then again, I would expect static behaviour from an in/out defined on an interface, and it seems that some people expect dynamic behaviour from in/out defined on classes so it's possible that it's just the behaviour people would expect. In any case I only suggested it as a possible middle ground. Personally I'd like to explore the idea that it might be less confusing and more correct to require the use of an interface to define in/out contracts. Would that be too much of a burden? Would it result in a huge number of "redundant" interfaces? Or, would it force people to write more correct code? I know I've see /a lot/ of code which would have been written better had someone had the forethought to design the interfaces/contracts first, and once they exist would have thought harder about changing them during initial development and beyond. Regan -- Using Opera's revolutionary email client: http://www.opera.com/mail/
May 09 2012