Thread overview
Suggestion: Allow Multiple Inheritance and still preserver super() functionality
Mar 06, 2008
Jim Gadrow
Mar 06, 2008
Russell Lewis
Mar 07, 2008
Mike Parker
Mar 07, 2008
Scott S. McCoy
Mar 08, 2008
Simen Kjaeraas
Mar 07, 2008
Jim Gadrow
March 06, 2008
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.
March 06, 2008
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.
March 07, 2008
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.
March 07, 2008
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.
March 07, 2008
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.
March 07, 2008
"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


March 08, 2008
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 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.


In Object Pascal, the keyword for calling methods from the parent (super) class is 'inherited'.


//-------

  Foo = class
    procedure doStuff();
  end;

  Bar = 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.doStuff();
  // 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', though.

-- Simen