August 03, 2007
Regan, good thing posting about that other older thread, I didn't know about it, and it was nice to see some actual arguments for the current behavior (even if I don't agree with them, but at least they can be argued), instead of the typical "it's the way it's done in C++"/"it's how C++ people want it done"...

The first argument is this one:
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=6975
(the one where with a series of cast, one can call a different method other than the "nearest" one in the method lineage hierarchy)
Indeed that's a problematic behavior, but it's not a language design error, it's a compiler bug! There has to be a way to make that work. Wasn't this option even considered or discussed? Did Walter just go right through to changing the language?


Regan Heath wrote:
> Steve Schveighoffer wrote:
> 
> Performance isn't the reason against the Java behaviour, here is Walters explaination:
> 
> <quote Walter quoting Stroustrup>
> 
> Stroustrup gives two examples (slightly modified here):
> 
> ---------------------------------
> class X1 { void f(int); }
> 
> // chain of derivations X(n) : X(n-1)
> 
> class X9: X8 { void f(double); }
> 
> void g(X9 p)
> {
>     p.f(1);    // X1.f or X9.f ?
> }
> -----------------------------------
> His argument is that one can easilly miss an overload of f() somewhere in a
> complex class heirarchy, and argues that one should not need to understand
> everything about a class heirarchy in order to derive from it. 

The second argument. This one is simply crap...
Stroustrup argues "that one should not need to understand everything about a class heirarchy in order to derive from it". Well, maybe you don't have to understand *everything* but you surely will have to understand *a lot*, even if you aren't changing a lot of behavior in the subclass. This is simply something you can't (or shouldn't) escape from: if you are changing the implementation, even if just a bit, you have to understand the rest of implementation, or you risk breaking things, no mater how boring or worksome it might be. This is not the same as simply *using* a class, where you only have to understand it's interface/contract, and encapsulation shields you from the implementation.
This is my rebuttal for the general case of this argument, but as for that particular example, it should also be noted, that with a decent IDE (in the likeness of JDT), the example is totally moot. You just place your mouse of the function call, and a text hover will show you the signature (and javadoc) of the function, thus immediately indicating which overload is being called, without having to manually search N levels of hierarchy to find which overload it is. Code completion will also allow listing all available overloads in an equally easy and convenient way. This is yet another case of people failing to understand how semantic code analysis tools (ie, IDE) can influence language productivity and design shortcomings.
(Minor plug, these two features, especially the last one, are not too far from being implemented in the Descent project)

>The other
> example involves operator=(), but since D doesn't allow overloading
> operator=() instead I'll rewrite it as if it were a function that needs to
> alter a class state, and a derived class written later that 'caches' a
> computation on the derived state:
> 
> class B
> {    long x;
>      void set(long i) { x = i; }
>     void set(int i) { x = i; }
>     long squareIt() { return x * x; }
> }
> class D : B
> {
>     long square;
>     void set(long i) { B.set(i); square = x * x; }
>     long squareIt() { return square; }
> }
> 
> Now, imagine B were a complex class with a lot of stuff in it, and our
> optimizing programmer missed the existence of set(int). Then, one has:
> 
>     long foo(B b)
>     {
>         b.set(3);
>         return b.squareIt();
>     }
> 
> and we have an obscure bug.
> 
> </quote>
> 

The third and final argument. This one at least, I agree that it is a valid concern. However, this should be solved in another way, not with the current D behavior (which clearly breaks the is-a relationship, a fixture of OO semantics).
One suggestion I agree with, is that if a subclass does not override *all* overloads of the same function name, then it is a compiler error. Then the above code would simply be an error, forcing the user to use an alias to bring down the upper class overloads, or write their own overloads.
For example, I've personally written visitor classes that in D would *silently* break with this behavior, and it wouldn't be easy to spot it, as well as very error prone in the face of changes. I could post the examples but it likely wouldn't matter to Walter anyway.

PS: Someone call to this thread those guys who think D design development is "community-driven"... ;)

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
August 04, 2007
Derek Parnell wrote:
> This is intentional and is due to D's simplistic lookup rules. Basically, D
> will look for a matching signature only in the class it self and not in any
> parent classes (its a little more complex than this but ...)

Essentially, D's behavior here matches C++'s. Steve is asking for Java style behavior. There are good arguments for both styles, and risks with both.

I prefer the C++ style because:

1) One can get the Java style behavior by using the alias declaration as outlined by Derek.

2) It avoids the problem of forgetting to override one of the base class overloads, which can be a hidden source of bugs.

3) A new overload can be added to the base class without screwing up the derived class.

4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.

5) I tend to view overloads as a tool that should be used sparingly, not as a substitute for having to think up a new name. In that vein, having a complex set of overloads is an indication that something might be wrong with the design.
August 04, 2007
Kirk McDonald wrote:
>>   alias super.foo foo;
> That's not a workaround, nor is this considered a problem. This is the intended behavior.

Yes, and it took some extra effort to make sure it worked that way! In other words, it is not accidental or incidental behavior.
August 04, 2007
Steve Schveighoffer wrote:
> Everyone who used the Y derivative suddenly finds their code doesn't compile.


This is actually good. Failing to compile means that a change in the class that might break things will not *silently* break things.
August 04, 2007
Aarti_pl wrote:
> ... and it's even not consistent with one of D design rules:
> 
> "If something looks similar as in C++, it should behave similar to C++."
> 
> I think that breaking this rule is main source of confusion here. In my opinion it would be much better for D to implement inheritance in a way similar to other C-like languages.

It does behave, in this instance, just like C++. Steve is asking for the Java behavior.
August 04, 2007
Regan Heath wrote:
> I could keep linking all day... these are all from the same thread so starting at the top and reading the whole thread should give you a fair idea of the issues.

All on one page:

http://www.digitalmars.com/d/archives/digitalmars/D/6928.html
August 04, 2007
Walter Bright wrote:

> Aarti_pl wrote:
>> ... and it's even not consistent with one of D design rules:
>> 
>> "If something looks similar as in C++, it should behave similar to C++."
>> 
>> I think that breaking this rule is main source of confusion here. In my opinion it would be much better for D to implement inheritance in a way similar to other C-like languages.
> 
> It does behave, in this instance, just like C++. Steve is asking for the Java behavior.

Yes you are right. I found that I was wrong just after Regan post with old threads concerning this issue.

There must be some other rule broken in this case, because I didn't know that in C++ behaviour is same. :-) Probably rule which is broken is just... programmer intuition...

Although current behaviour is not very intuitive I can live with it (knowing that there are arguments against intuitive behaviour). I think that it would be good idea to document it better (including few arguments why it was chosen over Java style behaviour).

-- 
Regards
Marcin Kuszczak (Aarti_pl)
-------------------------------------
Ask me why I believe in Jesus - http://zapytaj.dlajezusa.pl (en/pl)
Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/
-------------------------------------

August 04, 2007
Walter Bright wrote:
> 
> 4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.
> 

I'm not sure if you've seen my other reply to this argument, but let be more explicit, with pictures. Let's say you have such a big inheritance hierarchy, with overloads spread across it.

This is a picture of one using a tool to list all possible overloads, without have to look at EVERY base class:
http://web.ist.utl.pt/bruno.d.o.medeiros/dee/ide_overload_completion.png
It shows you the signatures of all overloads (as well as the class that they belong to).

This is a picture of one calling that function with a literal, and having the tool automatically list which overload is being called:
http://web.ist.utl.pt/bruno.d.o.medeiros/dee/ide_overload_lookup.png
The yellow popup shows the signature of the invoked overload, which we can see is the one with the long parameter. How easy! You don't even have to recall the specifics of the lookup rules of the language.
(note that that popup appears just by placing the mouse cursor over "set", there is not even a need to invoke an explicit command)

D's syntax makes it as easy to build these tools as Java, C#, Ruby, etc.. (which is *way* easier than doing it for C++)

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
August 04, 2007
Derek Parnell wrote:
> On Wed, 01 Aug 2007 16:47:12 -0400, Steve Schveighoffer wrote:
> 
>> I am wondering if the following behavior is intentional and if so, why. 
> 
> This is intentional and is due to D's simplistic lookup rules. Basically, D
> will look for a matching signature only in the class it self and not in any
> parent classes (its a little more complex than this but ...)
> 
> To 'fix' this situation you need to explicitly identify methods from parent
> classes that you wish to access from objects of the child class. This is
> done using an alias statement.
> 
> Add "alias X.foo foo;" to your class definition of Y.
> 
> class Y : X
> {
>   alias X.foo foo; // pulls in class X's foo name into this scope.
>   public int foo(int y)
>   {
>     return 3;
>   }
> }
> 
> Now it will compile and run.
> 
Well if you're going to make that kind of a requirement, why not allow multiple inheritance?  It would seem to resolve all the problems with "ambiguity in multiple paths of implementation".

(Also, it would be well if this were prominently documented. You did a very clear job, so copying that would be all that's necessary.  File it under inheritance.)
August 05, 2007
Bruno Medeiros wrote:
> Walter Bright wrote:
>>
>> 4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.
>>
> 
> I'm not sure if you've seen my other reply to this argument, but let be more explicit, with pictures. Let's say you have such a big inheritance hierarchy, with overloads spread across it.

I appreciate that you can write tools to analyze it. I think they help a lot in writing the code, but I don't think they are as helpful for auditing (code review).

Another problem with them is they are not part of the D compiler itself, and in my experience add on tools rarely get used, no matter how useful they are.