July 26, 2004
Walter wrote:

> 
> Why is simply omitting the redundant interface name in the inheritance list
> a problem? I don't get it.

Initially I was inclined to side with you here and suggest you simply make it an error to include a redundant interface name and finally put the matter to rest. However it is not that simple. Consider:
================================
interface Reads {
    void readSomething();
}

interface Writes {
    void writeSomething();
}

interface ReadsWrites {
    void readSomething();
    void writeSomething();
}

class Reader : Reads {
    void readSomething() { printf( "read1()" ); }
}

class Writer : Writer{
    void writeSomething() { printf( "write1()" ); }
}

class WritingReader : Reader, ReadingWriter {
    void writeSomething() { printf( "write2()" ); }
}

class ReadingWriter : Writer, ReadingWriter {
    void readSomething() { printf( "read2()" ); }
}
================
There are no redundant interfaces in this example and yet ReadingWriter and WritingReader are not considered valid implementations.

BUG NOTE!!!
The above code crashes the compiler when I try to compile it so you may want to note this as a bug. (win32 version)
July 26, 2004
parabolis wrote:

> Walter wrote:
> 
>>
>> Why is simply omitting the redundant interface name in the inheritance list
>> a problem? I don't get it.
> 
> 
> Initially I was inclined to side with you here and suggest you simply make it an error to include a redundant interface name and finally put the matter to rest. However it is not that simple. Consider:
> ================================
> interface Reads {
>     void readSomething();
> }
> 
> interface Writes {
>     void writeSomething();
> }
> 
> interface ReadsWrites {
>     void readSomething();
>     void writeSomething();
> }
> 
> class Reader : Reads {
>     void readSomething() { printf( "read1()" ); }
> }
> 
> class Writer : Writer{
>     void writeSomething() { printf( "write1()" ); }
> }
> 
> class WritingReader : Reader, ReadingWriter {
>     void writeSomething() { printf( "write2()" ); }
> }
> 
> class ReadingWriter : Writer, ReadingWriter {
>     void readSomething() { printf( "read2()" ); }
> }

I would expect this to not compile.  For example, if you had two classes that referred to each other in the extends/implements area, then you would have a circular reference.  This is really bad.  All inheritance structures should follow a DAG (Directed Acyclic Graph), and there are all kinds of ways of detecting them, etc.

For now, I have to assume that you meant to write ReadsWrites in place of ReadingWriter in the extends/implements area of the last two classes.  If it does not compile in this case, I would be inclined to agree with you.

I do realize that D is not Java, but it does seem very cumbersome to me to see a class have all methods necessary for an interface through inheritance, but it doesn't compile or run correctly.

Question:  wouldn't it be equally valid to have this:

interface ReadsWrites : Reads, Writes {}

wich would in turn have the readSomething() and writeSomething() methods inherited from the super-interfaces?

I would expect that (i.e. can have it in Java and C#, and, if you include pure virtual abstract classes as interfaces, in C++).
July 26, 2004
"Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:ce3mb4$1dn3$1@digitaldaemon.com...
> I actually got caught out with this one a few years back - in C++. I
thought the
> behavior was wrong then, and I still think it's wrong now. At the time, I
didn't
> realize that C++ was defined that way, I just assumed that the compiler
was
> crap. I had to workaround it by overriding the base class function in the derived class with an exact signature match, and defining the overridden function to call the base class function. That was a very time consuming
bug to
> find, and the workaround/fix was not very satisfying. C++ got this one
wrong.
> Java seems to have got it right. (In my humble opinion).

Inserting a 'using' declaration in C++ is the usual way to make this work, not creating wrappers. The using declaration in that case works analogously to the 'alias' declaration in D.


July 26, 2004
In article <ce3rao$1g1s$1@digitaldaemon.com>, parabolis says...
>
>Walter wrote:
>
>> 
>> Why is simply omitting the redundant interface name in the inheritance list a problem? I don't get it.
>
>Initially I was inclined to side with you here and suggest you simply
>make it an error to include a redundant interface name and finally put
>the matter to rest. However it is not that simple. Consider:
>================================
>interface Reads {
>     void readSomething();
>}
>
>interface Writes {
>     void writeSomething();
>}
>
>interface ReadsWrites {
>     void readSomething();
>     void writeSomething();
>}
>
>class Reader : Reads {
>     void readSomething() { printf( "read1()" ); }
>}
>
>class Writer : Writer{
>     void writeSomething() { printf( "write1()" ); }
>}
>
>class WritingReader : Reader, ReadingWriter {
>     void writeSomething() { printf( "write2()" ); }
>}
>
>class ReadingWriter : Writer, ReadingWriter {
>     void readSomething() { printf( "read2()" ); }
>}
>================
>There are no redundant interfaces in this example and yet ReadingWriter
>and WritingReader are not considered valid implementations.
>
>BUG NOTE!!!
>The above code crashes the compiler when I try to compile it so you may
>want to note this as a bug. (win32 version)

You have a recursive inheritance declaration with ReadingWriter.  Not that dmd should crash on it, but I suspect this is why :)  Assuming you meant WritingReader for the second definition then you've still got a circular inheritance heirarchy which is not allowed.  All that aside, should this really be legal?

interface I { void foo( char x ); }
class A { void foo( char x ) {} }
class B : A {}
class C : B {}
class D : C {}
class E : D {}
class F : E, I { void foo( int x ) {} }

Assume that all the above classes have very long definitions and that A, B, and C are in a third-party library with no documentation and convoluted, badly formatted import files.  As the creator of F I may very well have intended to define F.foo as the implementation for I.foo and just screwed up the prototype. With the existing lookup scheme this is flagged as an error but with the proposed scheme it would compile just fine.  Further assume that A.foo is for some completely different purpose as A may have no knowledge of interface I. IMO the proposed scheme will be the cause of some very hard to find bugs.


Sean


July 26, 2004
You're talking about spelling mistakes here Sean. You can make exactly the same case for everyday methods also (no Interfaces anywhere to be seen):

> class A { void foo ( char x ) {} }
> class B : A {}
> class C : B {}
> class D : C {}
> class E : D {}
> class F : E { void foo1 ( char x ) {} }

class G : F
{
    void ICantSpell()
    {
         super.foo ('!');
    }
}

Oops! I mistyped super.foo() when I really meant super.foo1() ... what a
silly billy

<g>



"Sean Kelly" <sean@f4.ca> wrote in message news:ce3vq3$1ht7$1@digitaldaemon.com...
> In article <ce3rao$1g1s$1@digitaldaemon.com>, parabolis says...
> >
> >Walter wrote:
> >
> >>
> >> Why is simply omitting the redundant interface name in the inheritance
list
> >> a problem? I don't get it.
> >
> >Initially I was inclined to side with you here and suggest you simply
> >make it an error to include a redundant interface name and finally put
> >the matter to rest. However it is not that simple. Consider:
> >================================
> >interface Reads {
> >     void readSomething();
> >}
> >
> >interface Writes {
> >     void writeSomething();
> >}
> >
> >interface ReadsWrites {
> >     void readSomething();
> >     void writeSomething();
> >}
> >
> >class Reader : Reads {
> >     void readSomething() { printf( "read1()" ); }
> >}
> >
> >class Writer : Writer{
> >     void writeSomething() { printf( "write1()" ); }
> >}
> >
> >class WritingReader : Reader, ReadingWriter {
> >     void writeSomething() { printf( "write2()" ); }
> >}
> >
> >class ReadingWriter : Writer, ReadingWriter {
> >     void readSomething() { printf( "read2()" ); }
> >}
> >================
> >There are no redundant interfaces in this example and yet ReadingWriter
> >and WritingReader are not considered valid implementations.
> >
> >BUG NOTE!!!
> >The above code crashes the compiler when I try to compile it so you may
> >want to note this as a bug. (win32 version)
>
> You have a recursive inheritance declaration with ReadingWriter.  Not that
dmd
> should crash on it, but I suspect this is why :)  Assuming you meant WritingReader for the second definition then you've still got a circular inheritance heirarchy which is not allowed.  All that aside, should this
really
> be legal?
>
> interface I { void foo( char x ); }
> class A { void foo( char x ) {} }
> class B : A {}
> class C : B {}
> class D : C {}
> class E : D {}
> class F : E, I { void foo( int x ) {} }
>
> Assume that all the above classes have very long definitions and that A,
B, and
> C are in a third-party library with no documentation and convoluted, badly formatted import files.  As the creator of F I may very well have intended
to
> define F.foo as the implementation for I.foo and just screwed up the
prototype.
> With the existing lookup scheme this is flagged as an error but with the proposed scheme it would compile just fine.  Further assume that A.foo is
for
> some completely different purpose as A may have no knowledge of interface
I.
> IMO the proposed scheme will be the cause of some very hard to find bugs.
>
>
> Sean
>
>


July 26, 2004
"Kris" <someidiot@earthlink.dot.dot.dot.net> wrote in news:ce26ue$cm1$1@digitaldaemon.com:

> Lars made a series of valid points in his three emails Walter; as have many others. The fact that you choose to divert focus by ignoring his core points does not serve you well. You have been asked to clarify the /benefits/ of the current implementation several times now, in various ways, and still you avoid the issue at hand. One might be given pause to think you're avoiding this at all costs ...

I don't get it.
Why should the *benefits* be clarified, as long as the disadvantages remain
unclear? Remember the old rule, if it's not broken, don't fix it.

> 
> Please; I ask you once again: show us /all/ why this C++ style method-hiding is necessary and/or beneficial to D programmers via one or two examples. As I've already stated, if the benefits are so obvious then it won't take much effort on your part to do so.

Consider that the benefits could be *hidden*, so there might be no easy way
to that. But I think, that Walter, already did that quite well.
Why should D take the look-up rules of a legacy language called Java?
Isn't it better to stick with the way modern languages like C++ and C# do it?


> Posting that example of how C++ operates had absolutely zero value: we already /know/ it works that way ~ that's exactly the point of this issue ~ there's no perceived need for it to be that way in D at all!

Disagree. It helped me better understand the D language and restrengthen my fading C++ skills. Futhermore some of those cool things you can do with C++ sprung back to my mind:

class SomeClass : Base
{
     alias Base.this this;  // inherit *all* constructors
}
[I don't expect that this compiles with DMD, but the D language should allow
it.]

How do you do that the 'Java way'?



Now some comments regarding your document (revision 4):

Chapter External Names:

Why is that chapter even still listed?



Chapter Satisfying Interface Contracts:

What's wrong with

class Foo : IFoo
{
   void foo()
   {}
}

class Bar : Foo, IBar
{
   void bar()
   {}
}

And why do present the other solutions first, that are not really solutions, since why are *different* from the original example?

<quote>
Now your class design is explicitly bound by the design of the contract. In
other words,
the implementation is tightly bound to the layout of the contract. Given that
the contract
is supposed to be independent from the implementation, this is a truly sad
place to be.
<quote/>

Why do you think that the *design* of the contract is of any importance? Generally, the *content* of the contract is of importance but not its design. I certainly could implement a class BarImpl2 in any way I could dream of, that fullfill's contract IBar and IFoo. Where's the catch, here?


Chapter The Alias peek-a-boo Game:

<quote>
Here’s why: If one wishes to limit the exposure of class methods, one uses an
Interface to represent only that portion of the concrete class that defines
the external contract.
<quote>

And how do I get an interface of the hidden class instance? I would have to use factories,  baggage, bloat, argh!


<quote>
Let’s recap some basic OO assumptions: a subclass is expected to inherit all
methods from its super-class, unless declared private therein. The compiler
is expected to provide override support where signatures match exactly. As
such, the required use of alias for this fundamental behavior is non-
intuitive, inappropriate, and defies commonly accepted OO practices.
<quote>

Personally, my OO assumption goes like this: A subclass must provide all the
methods of its super-classes. As matter of *convenience* compilers
implicitely take the method from the super-class to the subclass. (Except for
some cases, e.g. constructors)
But my point of view is rather non-intuitive, (mostly) inappropriate, and
certainly does not belongs to commonly accepted OO practices.


Farmer.
July 26, 2004
In article <ce414s$1icc$1@digitaldaemon.com>, Kris says...
>
>You're talking about spelling mistakes here Sean. You can make exactly the same case for everyday methods also (no Interfaces anywhere to be seen):
>
>> class A { void foo ( char x ) {} }
>> class B : A {}
>> class C : B {}
>> class D : C {}
>> class E : D {}
>> class F : E { void foo1 ( char x ) {} }
>
>class G : F
>{
>    void ICantSpell()
>    {
>         super.foo ('!');
>    }
>}
>
>Oops! I mistyped super.foo() when I really meant super.foo1() ... what a
>silly billy

hehe... It was an example, after all.  I imagine a real case would be much more complex.  What I was trying to point out was the risk of unintended consequences of the proposed lookup rules.  The existing rules are explicit: the interface for each class is well-defined and if the user wants to leverage existing code then he can do so explicitly using alias.  The result may be a bit more verbose but it's also far clearer IMO.  I grant that the risk of mistakes isn't as great as if D supported multiple inheritance, but I think it's definately still there. And looking at my example, does it really seem reasonble that the code *should* compile?  We're talking about a method that could be 10 levels up in the inheritance tree, and one which the implementer of F may not even know about. Perhaps I'm a control freak but I prefer my classes to have only the baggage I pack them with ;)


Sean


July 26, 2004
Walter wrote:
> 
> Sure. D originally worked the way you suggest. However, this message to me

My understanding of what Kris wants is both
  (1) inheritable interfaces _and_
  (2) no function hiding for interface satisfaction

Joe's message indicates that D originally satisfied (2) but not (1)
> --------------------------------------------
> The fact that interfaces are snapshots and not inherited from class to
> subclass
> can really mess you up sometimes.  Just tacking on the interface at each

Currently (1) is satisfied but not (2).

The reason Kris (rather indelicately) attacks the current
implementation is both because the current implementation
violates the inheritance aspect of the OO paradigm  and
because he does not see any valid justification for the
violation.

That the current implementation violates the inheritance
aspect of the OO paradigm should be evident. Classes inherit
except when satisfying interface requirements.

Violating some aspect of the OO paradigm is not condemning
in and of itself. As you pointed out in another post C++
also places restrictions on inheritance for the purpose of
resolving against multiple possibilities. I doubt any
non-trivial language can adhere perfectly to the OO paradigm.

What is condemning is a violation without a reason. Your
reason seems to be that 1) and 2) are not possible without
a typehole. If you are right then there can be no similar
language without the typehole. However Kris believes that
Java is similar enough language to be a counter example.

Since you have graciously made the relevant source public
Kris also has the option to make the required changes to
demonstrate the possibilty. I have an implementation in
mind that might work. However I am still finding my way
around the source and learning the basics behind OO
implementation.

Am I correct in assuming that dmd\src\phobos\internal\mars.h
accurately portrays the memory layout? (ie a pointer to a
Vtbl in memory points to a 32 bit value len which is followed
by a 32 bit pointer to len 32 bit pointers)

If so I am curious why an interface would need a Vtbl.
Actually the fact that there is an Interface struct at
all make me suspicious.

================================
(disclaimer - I have no idea how the following is actually
done. Any pointers to more info would be appreciated.)
================================

I am assuming instantiated objects contain a pointer to
their class's static data structure, which I am assuming
is what ClassInfo represents.

I am also assuming a Vtbl retains the super class' ordering
of functions and extends the table by adding its new
functions at the end and ovverides functions by replacing
the pointers in the super class' Vtbl with appropriate
pointers but otherwise copies the super class' Vtbl
verbtatim.

Finally I am assuming that ClassInfo's member baseClass
is a pointer to the super class.

Given these assumptions I would imagine interface data
could be kept by assining a unique ID to each interface
and keeping a table of all interfaces a class implements
so that if class B is a subclass of class A then B's
Interface table contains the union of the B's Interface
IDs and A's IDs.

For exampe if:
  A implements Interfaces with IDs 0, IDa, IDb and IDj,
  B implements Interfaces with IDs IDi, IDj, ... and IDk,
then:
  A's table [0,IDa,IDb,IDj]
  B's table [0,IDa,IDb,IDi,IDj,...,IDk]

A) Assigning an instantiated object, o, to an interface
variable, i, would entail three steps at runtime:
  1) verify that o has i's ID
  2) create space for i
  3) set i to point to the same thing o does

B) The casting operation for an interface would require
only one step at runtime:
  1) verify that o has i's ID

C) Assigning an instantiated object, o, to a class
variable, c, would entail three steps at runtime:
  1) verify that o can reach class c via baseclass
  2) create space for i
  3) set i to point to the same thing o does

D) The casting operation for a class would also
require only one step at runtime:
  1) verify that o can reach class c via baseclass

A), B), C) and D) above are the implict implementation.
Explicit casting/assigning forgoes the implicit cast
check.

If function calls are made using a constant offset
into the Vtbl in the ClassInfo that o has a pointer
to then Joe Battelle's type-hole example is not a
problem for this implementation. o's data is not
changed by any of the operations used in Joe's code,
so all the function calls are resolved with o's
ClassInfo.

Inherited functions can be allowed to satisfy an
interface and interfaces are inherited. Since the
ClassInfo interface table is a Set, redundant
interfaces are trivial.

Another benefit would seem to be that the ClassInfo
Interfaces hold in the current implemntation would
be eliminated.
July 26, 2004
<sigh>  Inline Farmer:

"Farmer"  wrote...
> I don't get it.
> Why should the *benefits* be clarified, as long as the disadvantages
remain
> unclear? Remember the old rule, if it's not broken, don't fix it.

You are welcome to your opinion, just as I am. However, you discredit the opinion of several others who have also stated their case regarding the disadvantages.

> Consider that the benefits could be *hidden*, so there might be no easy
way
> to that. But I think, that Walter, already did that quite well.
> Why should D take the look-up rules of a legacy language called Java?
> Isn't it better to stick with the way modern languages like C++ and C# do
it?

I'm tempted to giggle ... but I won't.

> Disagree. It helped me better understand the D language and restrengthen
my
> fading C++ skills. Futhermore some of those cool things you can do with
C++
> sprung back to my mind:

Glad to hear it! My comments are all made in context Farmer: you're simply splitting hairs.

> class SomeClass : Base
> {
>      alias Base.this this;  // inherit *all* constructors
> }
> [I don't expect that this compiles with DMD, but the D language should
allow
> it.]
>
> How do you do that the 'Java way'?

Finally; this has some merit. Shame it's illegal syntax in D. What you fail to grasp, Farmer, is that I don't have a problem with alias as a concept at all. I've just documented a case where I feel it's required use is totally non-intuitive.


> Now some comments regarding your document (revision 4):
>
> Chapter External Names:
>
> Why is that chapter even still listed?

Apparently, it must be for no reason other than to annoy you, Farmer. Don't you think it has some "educational" value? Just as example above had for you? Wouldn't you rather see the entire document simply disappear instead?


> Chapter Satisfying Interface Contracts:
>
> What's wrong with
>
> class Foo : IFoo
> {
>    void foo()
>    {}
> }
>
> class Bar : Foo, IBar
> {
>    void bar()
>    {}
> }

It works great for small examples. You're simply repeating what I've already noted.

> And why do present the other solutions first, that are not really
solutions,
> since why are *different* from the original example?

Forgive me; my parser just failed.


> Why do you think that the *design* of the contract is of any importance? Generally, the *content* of the contract is of importance but not its
design.
> I certainly could implement a class BarImpl2 in any way I could dream of, that fullfill's contract IBar and IFoo. Where's the catch, here?

You're absolutely right about the contract design. It should have no bearing whatsoever upon the class layout. This is exactly where the problem lies. As it is, If I construct a contract from several others, the class layout has to follow along in sych (as in the above example), unless you also provide a ream of stub & dispatch methods.

> Personally, my OO assumption goes like this: A subclass must provide all
the
> methods of its super-classes. As matter of *convenience* compilers implicitely take the method from the super-class to the subclass. (Except
for
> some cases, e.g. constructors)
> But my point of view is rather non-intuitive, (mostly) inappropriate, and
> certainly does not belongs to commonly accepted OO practices.

Your just splitting hairs dude. I agree with your more pedantic version, but this has nothing to do with the issue. The fact is that the D compiler does not implicitly do what you state.



July 26, 2004
I would tend to agree with you regarding compilation Sean <g>. My view is that name overrides should not be treated as a special case. If you're going to be specific, then why not require the programmer to explicitly bring in each and every single method they wish to expose from the super-class? At least it would be symmetrical, and therefore consistent and easy to grok.

However, although having to be explicit for *everything* would work beautifully regarding all these concerns, it might not meet with much agreement <g>

My issue is that currently there's this half-way house approach: all non-private methods are inherited, except those that happen to have a name that happens to be used by the subclass. It's a special-case, and those are always prone to failure (I believe Vathix has one such failure scenario regarding alias and override).

- Kris




"Sean Kelly" <sean@f4.ca> wrote in message news:ce41qm$1ij4$1@digitaldaemon.com...
> In article <ce414s$1icc$1@digitaldaemon.com>, Kris says...
> >
> >You're talking about spelling mistakes here Sean. You can make exactly
the
> >same case for everyday methods also (no Interfaces anywhere to be seen):
> >
> >> class A { void foo ( char x ) {} }
> >> class B : A {}
> >> class C : B {}
> >> class D : C {}
> >> class E : D {}
> >> class F : E { void foo1 ( char x ) {} }
> >
> >class G : F
> >{
> >    void ICantSpell()
> >    {
> >         super.foo ('!');
> >    }
> >}
> >
> >Oops! I mistyped super.foo() when I really meant super.foo1() ... what a
> >silly billy
>
> hehe... It was an example, after all.  I imagine a real case would be much
more
> complex.  What I was trying to point out was the risk of unintended
consequences
> of the proposed lookup rules.  The existing rules are explicit: the
interface
> for each class is well-defined and if the user wants to leverage existing
code
> then he can do so explicitly using alias.  The result may be a bit more
verbose
> but it's also far clearer IMO.  I grant that the risk of mistakes isn't as
great
> as if D supported multiple inheritance, but I think it's definately still
there.
> And looking at my example, does it really seem reasonble that the code
*should*
> compile?  We're talking about a method that could be 10 levels up in the inheritance tree, and one which the implementer of F may not even know
about.
> Perhaps I'm a control freak but I prefer my classes to have only the
baggage I
> pack them with ;)
>
>
> Sean
>
>