www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Suggestion: Allow Multiple Inheritance and still preserver super() functionality

reply Jim Gadrow <MakariVerslund gmail.com> writes:
Now, maybe I'm just ignorant because I've never written a compiler, but why
isn't it possible to allow for something like the example following in the
language?

I will first state that I don't like the super() function because I don't
believe the keyword 'super' very clearly identifies what is going on. wouldn't
parent() have been more suitable?

Anyways, the example:

import std.stdio;

interface A {
    void myFoo ();
}

interface B {
    void myBar ();
}

class C : A
{
    this ()
    {
        writefln ("Constructing a C...");
    }

    void myFoo ()
    {
        writefln ("I've been foo'd!");
    }
}

class D : B
{
    this ()
    {
        writefln ("Constructing a D...");
    }

    void myBar ()
    {
        writefln ("I've been bar'd!");
    }
}

class E : C, D
{
    this ()
    {
        parent.C ();    //Calls constructor of parent class C
        parent.D ();    //Calls constructor of parent class D
    }

    void myFooBar ()
    {
        myFoo ();
        myBar ();
    }
}

void main ()
{
    E myClass;
    myClass.myFooBar ();
}

Running the program should output:
Constructing a C...
Constructing a D...
I've been foo'd!
I've been bar'd!

Obviously, the same rule would apply for inheriting multiple classes as for
inheriting multiple interfaces in that something like:

class D : C, C

Would cause a compile error.
Mar 06 2008
next sibling parent Russell Lewis <webmaster villagersonline.com> writes:
Funny, just yesterday my professor gave a lecture on "why multiple 
inheritance seems like a good idea but is really hard to make work in 
practice."  (I'm in graduate school.)

The problem with MI isn't a syntax problem; there are a series of 
fundamental problems that are very hard to solve.  First is the "Diamond 
Problem," which I won't discuss here because Wikipedia has a good page 
on it:
	http://en.wikipedia.org/wiki/Diamond_problem

Another problem has to do with the binary layout of classes.  With 
single inheritance, you can define the binary layout of a class, and 
then require that any of its descendants preserve that layout.  That is, 
if you have
	class A { int x; }
	class B : A { int y; }

Then the binary layout of B is:
	+--------------------------+
	| exact copy of A's fields |
	+--------------------------+
	|      new B fields        |
	+--------------------------+

This makes it possible to convert a pointer-to-B to a pointer-to-A (and 
vice-versa) without any complexity or rewriting; it just works.  This is 
particularly important for methods, which the child might override, 
since you can call the methods using a pointer-to-A.  The calling 
function can pass the pointer-to-A as the implicit "this" pointer 
without knowing that the called function will interpret this as a 
pointer-to-B.  This works elegantly because the two pointers have the 
same binary representation.

Problem is, if a class has two parents, then you can't make that 
assumption.  You could include both of the parents whole, by value, but 
then you can't solve the diamond problem, and also you can't call 
overridden functions (because the "this" pointer for one of the parents 
is different than the "this" pointer of the child).

You can solve this, but it's costly in terms of language complexity and 
runtime performance.

Experience has shown that single-inheritance-with-interfaces is a much 
more practical solution for C-family languages.

Russ

Jim Gadrow wrote:
 Now, maybe I'm just ignorant because I've never written a compiler, but why
isn't it possible to allow for something like the example following in the
language?
 
 I will first state that I don't like the super() function because I don't
believe the keyword 'super' very clearly identifies what is going on. wouldn't
parent() have been more suitable?
 
 Anyways, the example:
 
 import std.stdio;
 
 interface A {
     void myFoo ();
 }
 
 interface B {
     void myBar ();
 }
 
 class C : A
 {
     this ()
     {
         writefln ("Constructing a C...");
     }
 
     void myFoo ()
     {
         writefln ("I've been foo'd!");
     }
 }
 
 class D : B
 {
     this ()
     {
         writefln ("Constructing a D...");
     }
 
     void myBar ()
     {
         writefln ("I've been bar'd!");
     }
 }
 
 class E : C, D
 {
     this ()
     {
         parent.C ();    //Calls constructor of parent class C
         parent.D ();    //Calls constructor of parent class D
     }
 
     void myFooBar ()
     {
         myFoo ();
         myBar ();
     }
 }
 
 void main ()
 {
     E myClass;
     myClass.myFooBar ();
 }
 
 Running the program should output:
 Constructing a C...
 Constructing a D...
 I've been foo'd!
 I've been bar'd!
 
 Obviously, the same rule would apply for inheriting multiple classes as for
inheriting multiple interfaces in that something like:
 
 class D : C, C
 
 Would cause a compile error.
Mar 06 2008
prev sibling next sibling parent reply Mike Parker <aldacron71 yahoo.com> writes:
Jim Gadrow wrote:

 
 I will first state that I don't like the super() function because I don't
believe the keyword 'super' very clearly identifies what is going on. 
 wouldn't parent() have been more suitable?
The term 'superclass' is more common in the OOP vernacular than 'parent class', so super() is spot on, IMO. You'll find the same used in Java and probably some other languages. Using 'parent()' would almost certainly cause some consternation among some D users, since the terms 'parent' and 'child' are often used to describe relationships in data structures. It's not uncommon to see methods like parent() (or getParent()) to fetch a parent node in a tree.
Mar 06 2008
next sibling parent "Scott S. McCoy" <tag cpan.org> writes:
On Fri, 07 Mar 2008 12:40:54 +0900, Mike Parker wrote:

 Jim Gadrow wrote:
 
 
 I will first state that I don't like the super() function because I
 don't believe the keyword 'super' very clearly identifies what is going
 on. wouldn't parent() have been more suitable?
The term 'superclass' is more common in the OOP vernacular than 'parent class', so super() is spot on, IMO. You'll find the same used in Java and probably some other languages. Using 'parent()' would almost certainly cause some consternation among some D users, since the terms 'parent' and 'child' are often used to describe relationships in data structures. It's not uncommon to see methods like parent() (or getParent()) to fetch a parent node in a tree.
Another issue with Multiple Inheritance is that in practice multiple inheritance is often unnecessary and rarely used correctly. More often than not, it's bastardized and used as a form of consuming dependencies as opposed to actually specifying inherent relationships.
Mar 07 2008
prev sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Fri, 07 Mar 2008 04:40:54 +0100, Mike Parker <aldacron71 yahoo.com>  =

wrote:

 Jim Gadrow wrote:

  I will first state that I don't like the super() function because I =
=
 don't believe the keyword 'super' very clearly identifies what is goi=
ng =
 on. wouldn't parent() have been more suitable?
The term 'superclass' is more common in the OOP vernacular than 'paren=
t =
 class', so super() is spot on, IMO. You'll find the same used in Java =
=
 and probably some other languages.

 Using 'parent()' would almost certainly cause some consternation among=
=
 some D users, since the terms 'parent' and 'child' are often used to  =
 describe relationships in data structures. It's not uncommon to see  =
 methods like parent() (or getParent()) to fetch a parent node in a tre=
e. In Object Pascal, the keyword for calling methods from the parent (super= ) = class is 'inherited'. //------- Foo =3D class procedure doStuff(); end; Bar =3D class(Foo) procedure doStuff(); overload; end; ... procedure Foo.doStuff(); begin // code here end; procedure Bar.doStuff(); begin inherited(); inherited doStuff(); // equivalent to above line, calls parent.doStuf= f(); // code here end; //------- I'm not saying I'm for including 'inherited' in D - 'super' fits me = perfectly. It may be clearer what it does than 'super' or 'parent', thou= gh. -- Simen
Mar 08 2008
prev sibling parent reply Jim Gadrow <MakariVerslund gmail.com> writes:
Thank you all for your responses. They have illuminated why
multiple-inheritance is less than optimal in terms of language implementation.

The only thing I will miss is the ability to allow a class to inherit fields
from multiple classes if it 'is' related to both classes. But, I suppose I
could do that via mixin, it just seems to add work-around code but at least now
I know the rationale behind it.
Mar 07 2008
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Jim Gadrow" wrote
 Thank you all for your responses. They have illuminated why 
 multiple-inheritance is less than optimal in terms of language 
 implementation.

 The only thing I will miss is the ability to allow a class to inherit 
 fields from multiple classes if it 'is' related to both classes. But, I 
 suppose I could do that via mixin, it just seems to add work-around code 
 but at least now I know the rationale behind it.
There are ways to do it with inner classes (without solving the diamond problem): class A { void a() {} } class B { void b() {} } class C : A { void a() {} class innerB : B { void b() { // can call a() or access C's members} C opCast() { return this.outer; } } // make it seamless private innerB _innerB; void b() { _innerB.b() } B opCast() { return _innerB;} } This really is just exposing the ugliness that compilers that support multiple inheritance hide :) But I'd say before doing something like this, the best thing to do is to try and use interfaces and encapsulation. -Steve
Mar 07 2008