www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A renewed call for interface methods with bodies

reply Burton Radons <burton-radons shaw.ca> writes:
I'd like to remind everyone about this lacking feature, why it's important, and
how the lack of it negatively impacts interface use. It's important for two
tasks: when you're writing an interface, and when you're using an interface
from someone else (through COM/CORBA/etc).

When it's your own interface, method bodies allow you to provide for default
implementations when the method can be implemented in terms of the abstract
interface. This can be vital if your interface has hundreds of methods. While
you can write a "base" implementation of the interface for others to inherit
from, it can be very confusing to convey to the implementor what methods are
not supported, requiring careful maintenance.

Because of this lack in various interface-using languages, interfaces tend to
have a limited number of methods. So when you use, for example, HTML's DOM, you
have insertBefore but not insertAfter. Without interface methods with bodies
(which aren't inserted into the vtable and would therefore be declared
"final"), you're stuck using a limited interface. That's not to mention that
sometimes interfaces have methods that would work beautifully with operator
overloading, but you can't use them in that way because you don't want to
rename the method or because it requires a slight tweak.

The only way to extend interfaces you don't own is through functions, and
function overloading beyond aliases for the same operation is inadvisable in
general and specifically denied by D's construction. So your only real option
is to use a naming system like "Node_insertAfter", leading to two completely
different ways to use the interface, attaching a redundant bit of notation to
the function name (and hoo boy is that a mouthful with many DirectX
interfaces), splitting the documentation, and disorganising the source file.
This is particularly bad with COM, which has an incompatible calling method
that requires wrapping every interface method in order to be safe.

Either way it's sloppy and inelegant, and it's not at all appropriate for a
modern language to look this shabby when doing something so basic. So we need
interface methods in two ways. One when you own the interface, and one where
you don't:

interface Foo
{
	int bar ();

	// Inserts it into the vtable, allowing it to be overloaded.
	int baz ()
	{
		return bar + bar;
	}

	// Not inserted into the vtable.
	final int boo ()
	{
		return baz * baz;
	}
}
Jan 24 2008
next sibling parent reply BCS <BCS pathlink.com> writes:
Burton Radons wrote:
[...]


something just occurred to me:

The only actions that a function body in an interfaces could do to 
"this" is function calls that would have to be virtual (or how could it 
ever do anything) however the code generated for a interface method 
needs to know the type of the object to be able to make the calls 
correctly. The only reasonable way I can see to let interface functions 
have bodies would be to in effect use a mixin.

Can anyone think of a better solution?
Jan 24 2008
next sibling parent reply Burton Radons <burton-radons shaw.ca> writes:
BCS Wrote:

 Burton Radons wrote:
 [...]
 
 
 something just occurred to me:
 
 The only actions that a function body in an interfaces could do to 
 "this" is function calls that would have to be virtual (or how could it 
 ever do anything) however the code generated for a interface method 
 needs to know the type of the object to be able to make the calls 
 correctly. The only reasonable way I can see to let interface functions 
 have bodies would be to in effect use a mixin.
 
 Can anyone think of a better solution?

There aren't any problems in compiling interface methods with bodies - it's exactly the same as a class with abstract methods. This part of the compiler should behave the same between interfaces and classes, it's just prevented from doing so by the compiler's semantic checker. This has never been a compiler issue (IIRC there were DMD versions which accidentally allowed interface methods with bodies), it's purely a language design mistake by Walter. I've been doing a LOT of COM interfacing the last half year and it's been painful every step of the way just because of this one little bit of missing polish.
Jan 24 2008
next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Burton Radons wrote:
 BCS Wrote:
 
 Burton Radons wrote:
 [...]


 something just occurred to me:

 The only actions that a function body in an interfaces could do to 
 "this" is function calls that would have to be virtual (or how could it 
 ever do anything) however the code generated for a interface method 
 needs to know the type of the object to be able to make the calls 
 correctly. The only reasonable way I can see to let interface functions 
 have bodies would be to in effect use a mixin.

 Can anyone think of a better solution?

There aren't any problems in compiling interface methods with bodies - it's exactly the same as a class with abstract methods. This part of the compiler should behave the same between interfaces and classes, it's just prevented from doing so by the compiler's semantic checker. This has never been a compiler issue (IIRC there were DMD versions which accidentally allowed interface methods with bodies), it's purely a language design mistake by Walter. I've been doing a LOT of COM interfacing the last half year and it's been painful every step of the way just because of this one little bit of missing polish.

There actually is a language issue: What if a class implements two interfaces that both declare the same method, but the class doesn't? Which one is called? BTW, I really need to stop checking this every 10 minutes...
Jan 24 2008
parent reply Burton Radons <burton-radons shaw.ca> writes:
Robert Fraser Wrote:

 There actually is a language issue: What if a class implements two 
 interfaces that both declare the same method, but the class doesn't? 
 Which one is called?
 
 BTW, I really need to stop checking this every 10 minutes...

This is a problem whether or not the methods are implemented, and there's a mechanism in place to deal with it that I can't remember as it's never happened to me.
Jan 24 2008
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Burton Radons wrote:
 Robert Fraser Wrote:
 
 There actually is a language issue: What if a class implements two 
 interfaces that both declare the same method, but the class doesn't? 
 Which one is called?

 BTW, I really need to stop checking this every 10 minutes...

This is a problem whether or not the methods are implemented, and there's a mechanism in place to deal with it that I can't remember as it's never happened to me.

Yes, but in that case the resolution mechanism doesn't really have to resolve anything. All the duplicated method pointers in the interfaces all eventually point to the same single implementation in the class that implements the interfaces. If you allow interfaces to have implementations then I think the compiler will have to enforce that any implemented methods have only one implementor. Otherwise you've got plain old multiple inheritance and you might as well just get rid of the 'interface' keyword. --bb
Jan 24 2008
parent reply Burton Radons <burton-radons shaw.ca> writes:
Bill Baxter Wrote:

 Burton Radons wrote:
 Robert Fraser Wrote:
 
 There actually is a language issue: What if a class implements two 
 interfaces that both declare the same method, but the class doesn't? 
 Which one is called?

 BTW, I really need to stop checking this every 10 minutes...

This is a problem whether or not the methods are implemented, and there's a mechanism in place to deal with it that I can't remember as it's never happened to me.

Yes, but in that case the resolution mechanism doesn't really have to resolve anything. All the duplicated method pointers in the interfaces all eventually point to the same single implementation in the class that implements the interfaces. If you allow interfaces to have implementations then I think the compiler will have to enforce that any implemented methods have only one implementor. Otherwise you've got plain old multiple inheritance and you might as well just get rid of the 'interface' keyword.

Hm, it seems there IS no conflict resolution: interface A { void foo (); } interface B { void foo (); } class C : A, B { void foo () { printf ("bar\n"); } } void main () { auto c = new C (); c.foo (); B b = c; b.foo (); A a = c; a.foo (); } This works and just prints "bar\nbar\nbar". I think that's a mistake; having the same name doesn't mean they have the same function, and it's more likely that the implementor made an error than that he actually meant to provide both with the same implementation. I should have to write C as: class C : A, B { void A.foo () { printf ("foo\n"); } void B.foo () { printf ("bar\n"); } } And then when when the object's posing as C call one or the other with: auto c = new C (); c.A.foo (); c.B.foo (); The method declaration is the way C# does it; but you need to cast to call each method (it would be illegal to call "c.foo" either way, which is correct). I don't care either way. I don't see what multiple inheritance has to do with this. Nothing about what I've suggested modifies the way D should handle function overloading at all, and if it does then I would want it to become stricter, not loosened.
Jan 24 2008
parent reply Burton Radons <burton-radons shaw.ca> writes:
(Hope this doesn't double-post)

Jesse Phillips Wrote:

 The problems of multiple inheritance is not related to overloaded 
 functions, but overridden functions. Whether that is what you meant or 
 not, that is still the problem. Your solution to the interface problem is 
 the exact solution that was avoid by not allowing multiple inheritance.

This has nothing to do with my proposal for interface methods with bodies (that is, it works correctly whether or not this problem is fixed), and I'm pretty sure the way it works now is a bug in the compiler, since there's no justification for it acting that way. Again and anon, my proposal does not modify overriding/overloading behaviour at all. It would if you could also use override: interface A { void foo (); } interface B : A { override void foo () { printf ("B\n"); } } interface C : A { override void foo () { printf ("C\n"); } } class D : B, C { } THEN you would have a diamond problem. But since I haven't suggested that, even that case doesn't apply. Please bring examples if you're going to counter as I have the feeling we're talking at cross-purposes.
 Also, on a side note, c.A.foo() is very unclear, because that suggest 
 that A is an object in c with a function foo() that you wish to call, 
 which is highly inaccurate because c is an A.

It's consistent with other usages of "." in the language when it's used to constrain scope, a la "std.string.find", and it's legal to have code that acts like that (object.class.object) already. I think you're a little late to be arguing for syntactually discriminating usages of ".". But as I said, I don't care as the issue hasn't come up in the going on six years I've been using D as my primary language.
Jan 25 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Jesse Phillips wrote:
 On Fri, 25 Jan 2008 03:31:11 -0500, Burton Radons wrote:
  
 This has nothing to do with my proposal for interface methods with
 bodies (that is, it works correctly whether or not this problem is
 fixed), and I'm pretty sure the way it works now is a bug in the
 compiler, since there's no justification for it acting that way. Again
 and anon, my proposal does not modify overriding/overloading behaviour
 at all. It would if you could also use override:

Please note that I'm not trying to say this is a bad thing to have, but that it imposes the same problem that is inherent with multiple inheritance. And I think the main point here is that the diamond problem is not the sole conflict with multiple inheritance. interface A { void foo(); } interface B { void foo() { writefln("I am B"); } } class C : A, B { } void main() { auto c = new C(); c.foo(); } You are claiming that your proposal just works without fixing the problem. Well, here's the problem, what does c.foo() call? C is said to have a function foo() because of A, which it does because of B. But in your _solution_ A.foo() is not B.foo(). And yes, you have in-fact provided a solution to the problem, which is you could not call c.foo(), but instead have to call c.A.foo(). With which C would then have to define void A.foo(){}; This I understand to be your solution to the multiple inheritance problem. This only solves the diamond problem, but not what is called by c.foo(); The current solution makes perfect sense as an interface is only there to say that this class has the function, since the class implementing both A and B has a void foo() then it has satisfied the claims of both. I realize this can be a problem if A and B expect foo() to do something completely different. But to this, I say redesign your class system. Your suggestion really adds more complexity into building a simple interface implementation. I would claim that this is worse merely because the problem where two or more interfaces being imported with the same function name being mutually exclusive is very rare (I could be wrong on this).

I completely agree with the above, and want to add that such a case ( a diamond ) should be a compiler warning/error IMO. You need to provide a foo method in the most derived class _with the override keyword_ and the compiler should complain if it's missing. if you need a default implementation use mixins as others already mentioned. no matter how many c++ programmers will complain the fact remains that MI is the wrong solution to the problem. inheritance does both sub-typing and sub-classing so D provides the proper solution of separating the two concepts. Interfaces provide sub-typing and mixins provide sub-classing. so the proper solution IMO to implement MI like behavior is to do somthing like that: --- interface A { void foo(); } template A_impl { void foo() { ... } } interface B { void foo(); } template B_impl { void foo() { ... } } class C : A, B { mixin A_impl Ai; mixin B_impl Bi; alias Ai.foo fooA; alias Bi.foo fooB; } void main() { auto test = new C(); test.fooA(); test.fooB(); } --- I agree it's probably a bit more verbose than the c++ version but: 1) this situation is rare. 2) D provides a more flexible mechanism so the programmer has _more_ control over the process. 3) no need to mutilate the language or the compiler with a very complex MI implementation. just my 2 euro cents Yigal
Jan 26 2008
prev sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Burton Radons escribió:
 BCS Wrote:
 
 Burton Radons wrote:
 [...]


 something just occurred to me:

 The only actions that a function body in an interfaces could do to 
 "this" is function calls that would have to be virtual (or how could it 
 ever do anything) however the code generated for a interface method 
 needs to know the type of the object to be able to make the calls 
 correctly. The only reasonable way I can see to let interface functions 
 have bodies would be to in effect use a mixin.

 Can anyone think of a better solution?

There aren't any problems in compiling interface methods with bodies - it's exactly the same as a class with abstract methods.

Doesn't that lead to multiple inheritance, with all its complexities?
Jan 24 2008
next sibling parent Burton Radons <burton-radons shaw.ca> writes:
Ary Borenszweig Wrote:

 Burton Radons escribió:
 BCS Wrote:
 
 Burton Radons wrote:
 [...]


 something just occurred to me:

 The only actions that a function body in an interfaces could do to 
 "this" is function calls that would have to be virtual (or how could it 
 ever do anything) however the code generated for a interface method 
 needs to know the type of the object to be able to make the calls 
 correctly. The only reasonable way I can see to let interface functions 
 have bodies would be to in effect use a mixin.

 Can anyone think of a better solution?

There aren't any problems in compiling interface methods with bodies - it's exactly the same as a class with abstract methods.

Doesn't that lead to multiple inheritance, with all its complexities?

Even if it did - and it won't, because D's function overloading prohibits these kind of games - it would be a tenth the complexity of C++'s MI, most of which is made complex because you need to handle inheritance diamonds of fields: struct A { int value; } struct B : A { void set16 () { value = 16; } } struct C : A { void set32 () { value = 32; } } struct D : B, C { } D d; d.set16 (); d.set32 (); // What is d.value? This requires some truly mind-melting compiling and arbitrary language design to allow it to work. But when limited to only methods, as in interfaces, it's a simple matter to do C++-style function overloading. However, that's not how D does function overloading; in D competition is not allowed between scopes, and I think that's fine and should be more rigorously enforced than it is.
Jan 24 2008
prev sibling parent BCS <BCS pathlink.com> writes:
Ary Borenszweig wrote:
 Burton Radons escribió:
 
 BCS Wrote:

 Burton Radons wrote:
 [...]


 something just occurred to me:

 The only actions that a function body in an interfaces could do to 
 "this" is function calls that would have to be virtual (or how could 
 it ever do anything) however the code generated for a interface 
 method needs to know the type of the object to be able to make the 
 calls correctly. The only reasonable way I can see to let interface 
 functions have bodies would be to in effect use a mixin.

 Can anyone think of a better solution?

There aren't any problems in compiling interface methods with bodies - it's exactly the same as a class with abstract methods.

Doesn't that lead to multiple inheritance, with all its complexities?

I think it would be more complex than the current implementation but not that complex. The issue is that with the current interfaces, the function pointers in the interface v-tbl are specific to the class that implements the interfaces. This results in the code expecting the this pointer to point to the start of the object where the class v-tbl is (or there abouts). For the proposed interfaces to work simply, the this pointer inside the un-overloaded function would have to point to the interface v-tbl (which IIRC is not at the start of the object, but inside overloaded functions, it would have to point the the start of the object. I can't think of any simple way to make this work. I'll grant it can work, I expect it would be somewhat complex though.
Jan 25 2008
prev sibling next sibling parent Jesse Phillips <jessekphillips gmail.com> writes:
On Fri, 25 Jan 2008 00:27:07 -0500, Burton Radons wrote:

 Bill Baxter Wrote:
 
 Burton Radons wrote:
 Robert Fraser Wrote:
 
 There actually is a language issue: What if a class implements two
 interfaces that both declare the same method, but the class doesn't?
 Which one is called?

 BTW, I really need to stop checking this every 10 minutes...

This is a problem whether or not the methods are implemented, and there's a mechanism in place to deal with it that I can't remember as it's never happened to me.

Yes, but in that case the resolution mechanism doesn't really have to resolve anything. All the duplicated method pointers in the interfaces all eventually point to the same single implementation in the class that implements the interfaces. If you allow interfaces to have implementations then I think the compiler will have to enforce that any implemented methods have only one implementor. Otherwise you've got plain old multiple inheritance and you might as well just get rid of the 'interface' keyword.

Hm, it seems there IS no conflict resolution: interface A { void foo (); } interface B { void foo (); } class C : A, B { void foo () { printf ("bar\n"); } } void main () { auto c = new C (); c.foo (); B b = c; b.foo (); A a = c; a.foo (); } This works and just prints "bar\nbar\nbar". I think that's a mistake; having the same name doesn't mean they have the same function, and it's more likely that the implementor made an error than that he actually meant to provide both with the same implementation. I should have to write C as: class C : A, B { void A.foo () { printf ("foo\n"); } void B.foo () { printf ("bar\n"); } } And then when when the object's posing as C call one or the other with: auto c = new C (); c.A.foo (); c.B.foo (); The method declaration is the way C# does it; but you need to cast to call each method (it would be illegal to call "c.foo" either way, which is correct). I don't care either way. I don't see what multiple inheritance has to do with this. Nothing about what I've suggested modifies the way D should handle function overloading at all, and if it does then I would want it to become stricter, not loosened.

The problems of multiple inheritance is not related to overloaded functions, but overridden functions. Whether that is what you meant or not, that is still the problem. Your solution to the interface problem is the exact solution that was avoid by not allowing multiple inheritance. Also, on a side note, c.A.foo() is very unclear, because that suggest that A is an object in c with a function foo() that you wish to call, which is highly inaccurate because c is an A.
Jan 24 2008
prev sibling parent Jesse Phillips <jessekphillips gmail.com> writes:
On Fri, 25 Jan 2008 03:31:11 -0500, Burton Radons wrote:
 
 This has nothing to do with my proposal for interface methods with
 bodies (that is, it works correctly whether or not this problem is
 fixed), and I'm pretty sure the way it works now is a bug in the
 compiler, since there's no justification for it acting that way. Again
 and anon, my proposal does not modify overriding/overloading behaviour
 at all. It would if you could also use override:

Please note that I'm not trying to say this is a bad thing to have, but that it imposes the same problem that is inherent with multiple inheritance. And I think the main point here is that the diamond problem is not the sole conflict with multiple inheritance. interface A { void foo(); } interface B { void foo() { writefln("I am B"); } } class C : A, B { } void main() { auto c = new C(); c.foo(); } You are claiming that your proposal just works without fixing the problem. Well, here's the problem, what does c.foo() call? C is said to have a function foo() because of A, which it does because of B. But in your _solution_ A.foo() is not B.foo(). And yes, you have in-fact provided a solution to the problem, which is you could not call c.foo(), but instead have to call c.A.foo(). With which C would then have to define void A.foo(){}; This I understand to be your solution to the multiple inheritance problem. This only solves the diamond problem, but not what is called by c.foo(); The current solution makes perfect sense as an interface is only there to say that this class has the function, since the class implementing both A and B has a void foo() then it has satisfied the claims of both. I realize this can be a problem if A and B expect foo() to do something completely different. But to this, I say redesign your class system. Your suggestion really adds more complexity into building a simple interface implementation. I would claim that this is worse merely because the problem where two or more interfaces being imported with the same function name being mutually exclusive is very rare (I could be wrong on this).
Jan 25 2008
prev sibling next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
Burton Radons wrote:
 I'd like to remind everyone about this lacking feature, why it's important,
and how the lack of it negatively impacts interface use. It's important for two
tasks: when you're writing an interface, and when you're using an interface
from someone else (through COM/CORBA/etc).
 
 When it's your own interface, method bodies allow you to provide for default
implementations when the method can be implemented in terms of the abstract
interface. This can be vital if your interface has hundreds of methods. While
you can write a "base" implementation of the interface for others to inherit
from, it can be very confusing to convey to the implementor what methods are
not supported, requiring careful maintenance.
 
 Because of this lack in various interface-using languages, interfaces tend to
have a limited number of methods. So when you use, for example, HTML's DOM, you
have insertBefore but not insertAfter. Without interface methods with bodies
(which aren't inserted into the vtable and would therefore be declared
"final"), you're stuck using a limited interface. That's not to mention that
sometimes interfaces have methods that would work beautifully with operator
overloading, but you can't use them in that way because you don't want to
rename the method or because it requires a slight tweak.
 
 The only way to extend interfaces you don't own is through functions, and
function overloading beyond aliases for the same operation is inadvisable in
general and specifically denied by D's construction. So your only real option
is to use a naming system like "Node_insertAfter", leading to two completely
different ways to use the interface, attaching a redundant bit of notation to
the function name (and hoo boy is that a mouthful with many DirectX
interfaces), splitting the documentation, and disorganising the source file.
This is particularly bad with COM, which has an incompatible calling method
that requires wrapping every interface method in order to be safe.
 
 Either way it's sloppy and inelegant, and it's not at all appropriate for a
modern language to look this shabby when doing something so basic. So we need
interface methods in two ways. One when you own the interface, and one where
you don't:
 
 interface Foo
 {
 	int bar ();
 
 	// Inserts it into the vtable, allowing it to be overloaded.
 	int baz ()
 	{
 		return bar + bar;
 	}
 
 	// Not inserted into the vtable.
 	final int boo ()
 	{
 		return baz * baz;
 	}
 }
 

Many of D interfaces have a reference implementation as a mixin: interface IFoo { void bar(); template Impl() { void bar() { // Do stuff } } } class Baz : IFoo { mixin IFoo.Impl!(); } It provides a standard, safe implementation that classes can choose to use or not, and allows safe multiple-inheritance like behavior.
Jan 24 2008
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Burton Radons wrote:
 I'd like to remind everyone about this lacking feature, why it's important,
and how the lack of it negatively impacts interface use. It's important for two
tasks: when you're writing an interface, and when you're using an interface
from someone else (through COM/CORBA/etc).

We have that, but you have to call it 'abstract class' rather than 'interface'. What you are asking for is essentially multiple inheritance -- that's the only difference between the two that I can see.
Jan 24 2008
parent reply BCS <BCS pathlink.com> writes:
Christopher Wright wrote:
 Burton Radons wrote:
 
 I'd like to remind everyone about this lacking feature, why it's 
 important, and how the lack of it negatively impacts interface use. 
 It's important for two tasks: when you're writing an interface, and 
 when you're using an interface from someone else (through COM/CORBA/etc).

We have that, but you have to call it 'abstract class' rather than 'interface'. What you are asking for is essentially multiple inheritance -- that's the only difference between the two that I can see.

these interfaces would have no data members.
Jan 25 2008
parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 25 Jan 2008 18:55:26 -0000, BCS <BCS pathlink.com> wrote:

 Christopher Wright wrote:
 Burton Radons wrote:

 I'd like to remind everyone about this lacking feature, why it's  
 important, and how the lack of it negatively impacts interface use.  
 It's important for two tasks: when you're writing an interface, and  
 when you're using an interface from someone else (through  
 COM/CORBA/etc).

'interface'. What you are asking for is essentially multiple inheritance -- that's the only difference between the two that I can see.

these interfaces would have no data members.

An interface is a set of related operations with no data members. An abstract base class may also define an interface. It may or may not partially implement the interface. It may or may not contain data members. The interface defined by the class could in principle be abstracted to an interface. Any misguided redefinitions of interfaces to include data members or implementations are in fact defining abstract base classes.
Jan 25 2008