May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857


Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|REOPENED                    |RESOLVED
         Resolution|                            |INVALID


--- Comment #22 from Walter Bright <bugzilla@digitalmars.com> 2012-05-03 17:49:32 PDT ---
Please stop reopening this.

> If you mean whether a given call is legal, then you could by the same argument
insist that called method names must be resolved in the context of the virtual type.

And they are. It's what the vtbl[] is for.

> (b) is going to continue to be exempt from this requirement.

You cannot widen the requirements of a function without providing an override of it. A.foo() cannot be called with the widened requirements of B.foo() - B.foo() gets called. That's why it can be overridden. IT WORKS CORRECTLY. The virtualness of the contracts is directly connected to the virtualness of the function calls.

As for the design decision on this, the decision was (and is) to implement classic OOP. It is theoretically sound. This is well trod and (I thought) well understood territory. As Andrei pointed out, it is not open for debate what OOP is.

I realize I am not very good at explaining this. I seriously recommend reading Meyer's book Object Oriented Programming. It's only $5.99.

If we try and implement alternate and incorrection notions of OOP, D will be considered a lightweight language run by amateurs.

It is entirely possible that:

1. I have seriously misunderstood OOP (I have made more than my share of such
mistakes before).

2. The OOP community has got it all wrong.

As evidence for (1), is there any OOP language that does it these other ways? Spec# does not, as Andrei researched and pointed out. For (2), you've got a high bar to overcome, and certainly have an opportunity for a groundbreaking academic paper.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #23 from Stewart Gordon <smjg@iname.com> 2012-05-03 18:21:44 PDT ---
(In reply to comment #22)
>> If you mean whether a given call is legal, then you could by the same argument insist that called method names must be resolved in the context of the virtual type.
> 
> And they are.  It's what the vtbl[] is for.

???

class A {}

class B : A {
    void foo() {}
}

void main() {
    A a = new B;
    a.foo();
}

You're claiming that this code is legal, and the penultimate line resolves to B's foo method???

>> (b) is going to continue to be exempt from this requirement.
> 
> You cannot widen the requirements of a function without providing an override of it.  A.foo() cannot be called with the widened requirements of B.foo() - B.foo() gets called.  That's why it can be overridden.

The concept of overriding in OOP as I've been brought up to understand it applies to what the method does, a quite different concept from what are legal inputs to it.

> As for the design decision on this, the decision was (and is) to implement classic OOP.  It is theoretically sound.  This is well trod and (I thought) well understood territory.  As Andrei pointed out, it is not open for debate what OOP is.

I'm surprised that the classic OOP spec covers the behaviour of contracts at all.  But maybe I just need to read up on it.

> As evidence for (1), is there any OOP language that does it these other ways?  Spec# does not, as Andrei researched and pointed out. For (2), you've got a high bar to overcome, and certainly have an opportunity for a groundbreaking academic paper.

At least I seem to have three potential co-authors already....

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #24 from Walter Bright <bugzilla@digitalmars.com> 2012-05-03 18:50:03 PDT ---
(In reply to comment #23)
> (In reply to comment #22)
> >> If you mean whether a given call is legal, then you could by the same argument insist that called method names must be resolved in the context of the virtual type.
> > 
> > And they are.  It's what the vtbl[] is for.
> 
> ???
> 
> class A {}
> 
> class B : A {
>     void foo() {}
> }
> 
> void main() {
>     A a = new B;
>     a.foo();
> }
> 
> You're claiming that this code is legal, and the penultimate line resolves to B's foo method???

No, I'm not. This thread is about overriding, not introducing, functions.


> I'm surprised that the classic OOP spec covers the behaviour of contracts at all.  But maybe I just need to read up on it.

Betrand Meyer's Object Oriented Programming. You can get it on amazon for $5.99. It's the definitive classic on the topic.

The behavior with contracts is just another aspect of the contravariance and covariance of derived objects.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #25 from Stewart Gordon <smjg@iname.com> 2012-05-03 19:02:13 PDT ---
(In reply to comment #24)
> No, I'm not. This thread is about overriding, not introducing, functions.

It's about introducing new legal inputs to a function.  Which is conceptually more like introducing a new function than overriding an existing function.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #26 from deadalnix <deadalnix@gmail.com> 2012-05-04 01:46:25 PDT ---
(In reply to comment #22)
> You cannot widen the requirements of a function without providing an override of it. A.foo() cannot be called with the widened requirements of B.foo() - B.foo() gets called. That's why it can be overridden. IT WORKS CORRECTLY. The virtualness of the contracts is directly connected to the virtualness of the function calls.
> 
> As for the design decision on this, the decision was (and is) to implement classic OOP. It is theoretically sound. This is well trod and (I thought) well understood territory. As Andrei pointed out, it is not open for debate what OOP is.

No it isn't. OOP doesn't say anything about contracts. The concept of contract is different and the question here is how both interact in a specific corner case.

> I realize I am not very good at explaining this. I seriously recommend reading Meyer's book Object Oriented Programming. It's only $5.99.

Is that book handle the corner case we are talking about here ? If it does, I'd be interested in what is the reasoning. The fact is that all document I read at this point do not say anything about this.

> If we try and implement alternate and incorrection notions of OOP, D will be considered a lightweight language run by amateurs.
> 

I do agree, but once again, OOP isn't about contracts.

> It is entirely possible that:
> 
> 1. I have seriously misunderstood OOP (I have made more than my share of such
> mistakes before).
> 
> 2. The OOP community has got it all wrong.
> 

You perfectly know that both are unlikely. Stating this avoid any interesting debate. As for now, we have 25 comment, and not once mention any argument in favor of keeping the standard behavior instead of the proposed one.

> As evidence for (1), is there any OOP language that does it these other ways? Spec# does not, as Andrei researched and pointed out. For (2), you've got a high bar to overcome, and certainly have an opportunity for a groundbreaking academic paper.

I read many paper on the subject. None was covering the corner case we are talking about here. Without this corner case, both solutions satisfy requirements. However, the chosen one is simpler (in contract can simply be added to the function body).

I'd be happy to see ONE argument on favor of the current behavior. Everybody is doing it isn't a valid argument. We are D, we know everybody is using C++ and PHP (among others), and we do also know that such languages are horribly crippled by all sort of crazyness. Everybody is doing it isn't an argument.

PS: Note that in case of Spec#, the decision is made to DISALLOW override of in contracts. In such a situation, both behavior proposed in this thread are equivalent, and so, it is not a relevant example.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #27 from Andrei Alexandrescu <andrei@metalanguage.com> 2012-05-04 04:03:36 PDT ---
(In reply to comment #26)
> No it isn't. OOP doesn't say anything about contracts. The concept of contract is different and the question here is how both interact in a specific corner case.

Walter's explanation creates confusion because he uses the wrong vocabulary (i.e. OOP instead of Design by Contract).

> > I realize I am not very good at explaining this. I seriously recommend reading Meyer's book Object Oriented Programming. It's only $5.99.
> 
> Is that book handle the corner case we are talking about here ?

Yes. There's another book on the subject called "Design by Contract, by Example". I wouldn't recommend it as a good book in general but it does teach the topic.

> If it does, I'd
> be interested in what is the reasoning.

Well it's not all that reasonable to expect someone to essentially copy the text from the book or summarize it for you.

> The fact is that all document I read at
> this point do not say anything about this.

It means you are reading the wrong documents. It takes literally under a minute to find relevant documents all over.

> > As evidence for (1), is there any OOP language that does it these other ways? Spec# does not, as Andrei researched and pointed out. For (2), you've got a high bar to overcome, and certainly have an opportunity for a groundbreaking academic paper.
> 
> I read many paper on the subject. None was covering the corner case we are talking about here. Without this corner case, both solutions satisfy requirements. However, the chosen one is simpler (in contract can simply be added to the function body).

There's no reason to doubt you are telling the truth, so this must be quite an interesting case of confirmation bias as you seem to have read only what doesn't infirm your hypothesis and glossed over everything that does.

As an example, google for

"design by contract" inheritance

The literally FIRST hit takes to a slide deck, see http://goo.gl/544fU. There there is theory and examples on how contracts work.

> I'd be happy to see ONE argument on favor of the current behavior. Everybody is doing it isn't a valid argument.

It's a very valid argument. DbC is established. People learn about DbC from various places and they come to apply it in D. They'd be surprised and annoyed that D doesn't do what it's supposed to do. There are of course other arguments, which you can find in the literature. This is not the time and place for a course in DbC.

> We are D, we know everybody is using C++ and
> PHP (among others), and we do also know that such languages are horribly
> crippled by all sort of crazyness. Everybody is doing it isn't an argument.

On the other hand many are also doing some good things so gratuitously not doing them doesn't help, either.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #28 from Don <clugdbug@yahoo.com.au> 2012-05-04 04:48:20 PDT ---
(In reply to comment #27)
> (In reply to comment #26)
> There's no reason to doubt you are telling the truth, so this must be quite an
> interesting case of confirmation bias as you seem to have read only what
> doesn't infirm your hypothesis and glossed over everything that does.
> 
> As an example, google for
> 
> "design by contract" inheritance
> 
> The literally FIRST hit takes to a slide deck, see http://goo.gl/544fU. There there is theory and examples on how contracts work.

Andrei, those slides don't address the issue at all.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #29 from Andrei Alexandrescu <andrei@metalanguage.com> 2012-05-04 05:35:12 PDT ---
(In reply to comment #28)
> > The literally FIRST hit takes to a slide deck, see http://goo.gl/544fU. There there is theory and examples on how contracts work.
> 
> Andrei, those slides don't address the issue at all.

The issue as exemplified herein is:

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

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

void fizzbuzz(A a) {
    a.foo(-1);
}

The question is, why does the code work when A's contract is in fact violated. Slide 22-10 in that deck gives as example a method invert(). The base class has precondition epsilon >= 10^(– 6) and the derived class has precondition epsilon
>= 10^(– 20). The surrounding slides explain rather copiously how a call to
invert against the derived class works even though the precondition of the base class is violated (e.g. by passing epsilon = 10^(– 10). The example given in the slides has a straightforward correspondence to the one above.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #30 from Don <clugdbug@yahoo.com.au> 2012-05-04 05:57:39 PDT ---
(In reply to comment #29)
> (In reply to comment #28)
> > > The literally FIRST hit takes to a slide deck, see http://goo.gl/544fU. There there is theory and examples on how contracts work.
> > 
> > Andrei, those slides don't address the issue at all.
> 
> The issue as exemplified herein is:
> 
> class A {
>     void foo(int x) in { assert(x > 0); } body {}
> }
> 
> class B : A {
>     void foo(int x) in { assert(x > -2); } body {}
> }
> 
> void fizzbuzz(A a) {
>     a.foo(-1);
> }
> 
> The question is, why does the code work when A's contract is in fact violated. Slide 22-10 in that deck gives as example a method invert(). The base class has precondition epsilon >= 10^(– 6) and the derived class has precondition epsilon
> >= 10^(– 20). The surrounding slides explain rather copiously how a call to
> invert against the derived class works even though the precondition of the base class is violated (e.g. by passing epsilon = 10^(– 10). The example given in the slides has a straightforward correspondence to the one above.

You are making a massive assumption here, which I don't see in the slides. The
assumption is that fizzbuzz gets access to B's weakened precondition.
I've looked all over the Eiffel website and haven't seen anything which implies
that. Rather, everything I read implies that it's a contract -- it's the
clients responsibility to ensure that the precondition is satisfied. fizzbuzz
doesn't have a contract with B, it doesn't even know that B exists.

fizzbuzz() clearly has a bug. It will fail if given an A which isn't actually a
B.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
May 04, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=6857



--- Comment #31 from Andrei Alexandrescu <andrei@metalanguage.com> 2012-05-04 06:21:14 PDT ---
(In reply to comment #30)
> You are making a massive assumption here, which I don't see in the slides. The
> assumption is that fizzbuzz gets access to B's weakened precondition.
> I've looked all over the Eiffel website and haven't seen anything which implies
> that. Rather, everything I read implies that it's a contract -- it's the
> clients responsibility to ensure that the precondition is satisfied. fizzbuzz
> doesn't have a contract with B, it doesn't even know that B exists.

I understand the distinction, and how the slides doesn't make it clear that the precondition is dynamically bound. In fact the author found it so obvious as to be unnecessary to mention (generally in the OOP world all methods are dynamically bound unless expressly noted otherwise; C++ is an anomaly). The second hit, http://goo.gl/5r7BF, clarifies with the diagrams on slides 17, 18, 20 and with the associated text that client code interacts with the base class only.

The ultimate proof would be to read Meyer's book on DbC (or the other one) and/or download the Eiffel compiler and compile an example.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------