Thread overview
Interface behavior
Aug 21, 2002
Joe Battelle
Aug 21, 2002
Walter
Aug 21, 2002
Joe Battelle
Aug 21, 2002
Walter
Aug 21, 2002
Joe Battelle
Aug 21, 2002
Joe Battelle
Aug 21, 2002
Walter
Aug 21, 2002
Joe Battelle
Aug 23, 2002
Joe Battelle
Aug 23, 2002
Walter
August 21, 2002
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 level in the hierarchy doesn't work either--you get a unique interface each time. Casting to a superclass before casting to the interface that the derived type supports can alter which version of the interface is called.  The spec doesn't say much about this.

Are we stuck with this feature of interfaces or will they be inheritable at some point?

Example code:
-------------
import c.stdio;

interface D {
abstract int foo();
}

class A : D {
int foo() { return 1; }
}

class B : A, D { //carry the D's, 'cause they're not inherited
//this should tell you something's up :)
//A's foo is taken to satisfy D.
//a cast(D) on a B will always use this foo
//even if the B is actually a further derived type with overloaded foo()
}

class C : B, D {
int foo() { return 2; }
}

int main (char[][] args)
{
C c = new C;
A a = (A) c;
int j = a.foo(); // 2
B b = (B) c;
int k = b.foo(); // 2
D d1 = (D) c;
int l = d1.foo(); // 2
D d2 = (D) b;
int m = d2.foo(); // 1 Gotcha!!!  b.foo() not equal to (D)b.foo()

//prints 2 2 2 1
printf("%d %d %d %d", j, k, l, m);

return 0;
}


August 21, 2002
There is a bug here, but I also want to ask what should happen with both A and C implement D?

"Joe Battelle" <Joe_member@pathlink.com> wrote in message news:ajv33k$2ntc$1@digitaldaemon.com...
> 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
level
> in the hierarchy doesn't work either--you get a unique interface each
time.
> Casting to a superclass before casting to the interface that the derived
type
> supports can alter which version of the interface is called.  The spec
doesn't
> say much about this.
>
> Are we stuck with this feature of interfaces or will they be inheritable
at some
> point?
>
> Example code:
> -------------
> import c.stdio;
>
> interface D {
> abstract int foo();
> }
>
> class A : D {
> int foo() { return 1; }
> }
>
> class B : A, D { //carry the D's, 'cause they're not inherited
> //this should tell you something's up :)
> //A's foo is taken to satisfy D.
> //a cast(D) on a B will always use this foo
> //even if the B is actually a further derived type with overloaded foo()
> }
>
> class C : B, D {
> int foo() { return 2; }
> }
>
> int main (char[][] args)
> {
> C c = new C;
> A a = (A) c;
> int j = a.foo(); // 2
> B b = (B) c;
> int k = b.foo(); // 2
> D d1 = (D) c;
> int l = d1.foo(); // 2
> D d2 = (D) b;
> int m = d2.foo(); // 1 Gotcha!!!  b.foo() not equal to (D)b.foo()
>
> //prints 2 2 2 1
> printf("%d %d %d %d", j, k, l, m);
>
> return 0;
> }
>
>


August 21, 2002
In article <ajvc6e$31e0$1@digitaldaemon.com>, Walter says...
>
>There is a bug here, but I also want to ask what should happen with both A and C implement D?
Well I would rather see the interface truly inherited so you don't need to (can't?) specify it at more than one level in the hierarchy--but maybe there's an implementation obstacle to this?

Otherwise, you just get deeper and deeper... Right now there is no compile-time enforcement of interface specifications, so I'm not absolutely sure what you're intending for them :)  Interfaces seem to be unique for each subclass, and seem to be built from the point of view in the inheritance graph of the derived class.  This makes it look like interfaces themselves are inherited and can be overriden, especially in the code below, where you can sandwich a class not having the interface between two that do--and yet the sandwiched class has an effect on the interface of the final derived class (that specifies, but doesn't implement the interface.)  On the other hand, right now you can create holes in the interface graph as the crash on use of the sandwiched class directly shows.
----------------
import c.stdio;

interface D {
abstract int foo();
}

class A : D {
int foo() { printf("A"); return 0; }
}

class B : A {
int foo() { printf("B"); return 1; }
}

class C : B, D {
//this hides B's foo--note altered return type
void foo() { printf("C"); }
}

int main (char[][] args)
{
C c = new C;
D d = (D)c;
c.foo();    //C--makes sense; we're hiding D's foo
d.foo();    //B--Whoah! The D interface takes a snapshot at C of the
//inheritance hierarchy bringing in to the interface whatever
//matches exactly.

//OK, so B participates in C's D, but does B inherit D from A?
B b = new B;
if ((D) b) { //This succeeds so B must
printf("X");
D d = (D) b;
version(crash) {
d.foo();  //CRASH; guess not
//Should this be A?
}
}

//while I'm at it:
D d1 = new D; //what does that do? compiles fine.

return 0;
}




August 21, 2002
Ok, so are you saying that interfaces should be "virtually" inherited, i.e. no matter how many times they appear in the inheritance heirarchy there is only one instance of them?

"Joe Battelle" <Joe_member@pathlink.com> wrote in message news:ajvq0n$gpf$1@digitaldaemon.com...
> In article <ajvc6e$31e0$1@digitaldaemon.com>, Walter says...
> >
> >There is a bug here, but I also want to ask what should happen with both
A
> >and C implement D?
> Well I would rather see the interface truly inherited so you don't need to (can't?) specify it at more than one level in the hierarchy--but maybe
there's
> an implementation obstacle to this?
>
> Otherwise, you just get deeper and deeper... Right now there is no
compile-time
> enforcement of interface specifications, so I'm not absolutely sure what
you're
> intending for them :)  Interfaces seem to be unique for each subclass, and
seem
> to be built from the point of view in the inheritance graph of the derived class.  This makes it look like interfaces themselves are inherited and
can be
> overriden, especially in the code below, where you can sandwich a class
not
> having the interface between two that do--and yet the sandwiched class has
an
> effect on the interface of the final derived class (that specifies, but
doesn't
> implement the interface.)  On the other hand, right now you can create
holes in
> the interface graph as the crash on use of the sandwiched class directly
shows.
> ----------------
> import c.stdio;
>
> interface D {
> abstract int foo();
> }
>
> class A : D {
> int foo() { printf("A"); return 0; }
> }
>
> class B : A {
> int foo() { printf("B"); return 1; }
> }
>
> class C : B, D {
> //this hides B's foo--note altered return type
> void foo() { printf("C"); }
> }
>
> int main (char[][] args)
> {
> C c = new C;
> D d = (D)c;
> c.foo();    //C--makes sense; we're hiding D's foo
> d.foo();    //B--Whoah! The D interface takes a snapshot at C of the
> //inheritance hierarchy bringing in to the interface whatever
> //matches exactly.
>
> //OK, so B participates in C's D, but does B inherit D from A?
> B b = new B;
> if ((D) b) { //This succeeds so B must
> printf("X");
> D d = (D) b;
> version(crash) {
> d.foo();  //CRASH; guess not
> //Should this be A?
> }
> }
>
> //while I'm at it:
> D d1 = new D; //what does that do? compiles fine.
>
> return 0;
> }
>
>
>
>


August 21, 2002
>Ok, so are you saying that interfaces should be "virtually" inherited, i.e. no matter how many times they appear in the inheritance heirarchy there is only one instance of them?

No, I think that is too restrictive.

These are the rules I want to see.  They are close to what you are doing now.
(1) & (3) add compile-time restrictions to your current implementation to close
up the holes I demonstrated earlier.  (2) fixes what I think is a bug/limitation
in your current implementation.


1) Strict specification -- if a D class implements an interface, then the interface must be fully satisfied through methods in the inheritance graph up to and including that class, otherwise you get a compile-time error.

interface D { void foo(); }
class X : D { char[] foo(); } //ERROR, X doesn't implement D
class A { void foo(); }
class B : D {} //OK, B satisfies D through A's methods

2) Inheritance of Interfaces -- if a D class implements an interface, then all subclasses of the class will also implement the interface.

interface D { void foo(); }
class A { void foo(); }
class B : A {}

This means (D) B is legal.

3) Overriding Interfaces -- if a subclass of a class that implements an
interface, overrides a member of that interface then it is a compile-time error,
unless the class specifies that it is redefining the interface, whereby a new
interface is created from that point in the hierarchy.
interface D { void foo(); }
class A : D { void foo() {} }
class B : A { void foo() {} } //ERROR, redefining D without specifying it
class C : B, D { void foo() {} } //OK, we get a new interface here
class C : B, D { char[] foo() {} } //ERROR, we didn't override anything in D

This will keep your current behavior, but prohibit the bad apples I showed in my other posts.


August 21, 2002
Sorry--typo, the example code for rule 2 should have been:

interface D { void foo(); }
class A : D { void foo(); } //A implements D
class B : A {} //so does B through inheritance

B b;
(D) b is legal.


August 21, 2002
"Joe Battelle" <Joe_member@pathlink.com> wrote in message news:ak0o22$2opb$1@digitaldaemon.com...
> 1) Strict specification -- if a D class implements an interface, then the interface must be fully satisfied through methods in the inheritance graph
up to
> and including that class, otherwise you get a compile-time error.
>
> interface D { void foo(); }
> class X : D { char[] foo(); } //ERROR, X doesn't implement D

Should already be doing that.

> class A { void foo(); }
> class B : D {} //OK, B satisfies D through A's methods

That's the virtual inheritance thing.

> 2) Inheritance of Interfaces -- if a D class implements an interface, then
all
> subclasses of the class will also implement the interface.
>
> interface D { void foo(); }
> class A { void foo(); }

I think you meant class A:D{void foo(); }

> class B : A {}
>
> This means (D) B is legal.

Yes.

> 3) Overriding Interfaces -- if a subclass of a class that implements an interface, overrides a member of that interface then it is a compile-time
error,
> unless the class specifies that it is redefining the interface, whereby a
new
> interface is created from that point in the hierarchy.
> interface D { void foo(); }
> class A : D { void foo() {} }
> class B : A { void foo() {} } //ERROR, redefining D without specifying it

I guess I don't agree.

> class C : B, D { void foo() {} } //OK, we get a new interface here
> class C : B, D { char[] foo() {} } //ERROR, we didn't override anything in
D

I tentatively agree.

> This will keep your current behavior, but prohibit the bad apples I showed
in my
> other posts.

I think the bad apple is just a bug in the implementation.


August 21, 2002
>> class X : D { char[] foo(); } //ERROR, X doesn't implement D
>
>Should already be doing that.
Right, it doesn't, so it's a bug.

>
>> This means (D) B is legal.
>
>Yes.
Doesn't work though, so that's a bug too.

>> interface D { void foo(); }
>> class A : D { void foo(); }
>> class B : A { void foo() {} } //ERROR, redefining D without specifying it
>I guess I don't agree.
If you can make (D)b.foo() = b.foo() great, the restriction is not absolutely
necessary.  On the otherhand I like it because it shows you where in the graph,
D can actually change behavior.

Thanks for your responses.


August 23, 2002
>> This means (D) B is legal.
>
>Yes.

More bugs for the unittests when interfaces are fixed:

interface D { int foo(); }
class A : D { int foo() { return 1; } }
class B : A { int foo() { return 2; }
D me() { return this; } }
unittest()
{
A a = new A;
B b = new B;
assert(b.me().foo() != a.foo());
}

Currently, this gives compilation error: "cannot implicitly convert B to D."

Also, as would be expected if you define me() up in A, you get no compile time
error, but the current implementation uses A's foo() instead of B's foo() on a
call such as b.me().foo().


August 23, 2002
Yes, having a set of executable tests to illustrate and validate correct behavior is very nice. Thanks!


"Joe Battelle" <Joe_member@pathlink.com> wrote in message news:ak44i2$32d$1@digitaldaemon.com...
> More bugs for the unittests when interfaces are fixed:
>
> interface D { int foo(); }
> class A : D { int foo() { return 1; } }
> class B : A { int foo() { return 2; }
> D me() { return this; } }
> unittest()
> {
> A a = new A;
> B b = new B;
> assert(b.me().foo() != a.foo());
> }
>
> Currently, this gives compilation error: "cannot implicitly convert B to
D."
>
> Also, as would be expected if you define me() up in A, you get no compile
time
> error, but the current implementation uses A's foo() instead of B's foo()
on a
> call such as b.me().foo().
>
>