www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - "The last feature": overridable methods in interfaces

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Walter has now implemented final methods in interfaces and also 
contracts in interfaces, both of which I think are just awesome.

We figured that essentially he artificially disallows interfaces from 
providing bodies for methods. I think that's a gratuitous limitation; 
the only distinguishing quality of an interface is that it has no state. 
  Other than that, interfaces can always offer overridable functions 
that by default offer functionality in terms of existing interface 
functions. For example:

interface Stack(T)
{
     void push(T);
     void pop();
      property ref T top();
      property bool empty();
     T belowTop()
     {
         auto t = top;
         pop();
         auto result = top;
         push(t);
     }
}

The default implementation of belowTop does a fair amount of work. A 
particular implementation might just use that or override it with a more 
efficient implementation.

Many more examples can be imagined, but I'm looking for a killer one, or 
perhaps a killer counterexample (e.g. when would an interface-defined 
method be really bad?)

Your thoughts welcome.


Andrei
Feb 07 2010
next sibling parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also 
 contracts in interfaces, both of which I think are just awesome.
 
 We figured that essentially he artificially disallows interfaces from 
 providing bodies for methods. I think that's a gratuitous limitation; 
 the only distinguishing quality of an interface is that it has no state. 
  Other than that, interfaces can always offer overridable functions that 
 by default offer functionality in terms of existing interface functions. 
 For example:
 
 interface Stack(T)
 {
     void push(T);
     void pop();
      property ref T top();
      property bool empty();
     T belowTop()
     {
         auto t = top;
         pop();
         auto result = top;
         push(t);
     }
 }
 
 The default implementation of belowTop does a fair amount of work. A 
 particular implementation might just use that or override it with a more 
 efficient implementation.
 
 Many more examples can be imagined, but I'm looking for a killer one, or 
 perhaps a killer counterexample (e.g. when would an interface-defined 
 method be really bad?)
 
 Your thoughts welcome.

What happens when two interfaces implement the same method? I thought multiple subtyping without multiple inheritance was the raison d'Ítre for interfaces. -Lars
Feb 08 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Lars T. Kyllingstad wrote:
 Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also 
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces from 
 providing bodies for methods. I think that's a gratuitous limitation; 
 the only distinguishing quality of an interface is that it has no 
 state.  Other than that, interfaces can always offer overridable 
 functions that by default offer functionality in terms of existing 
 interface functions. For example:

 interface Stack(T)
 {
     void push(T);
     void pop();
      property ref T top();
      property bool empty();
     T belowTop()
     {
         auto t = top;
         pop();
         auto result = top;
         push(t);
     }
 }

 The default implementation of belowTop does a fair amount of work. A 
 particular implementation might just use that or override it with a 
 more efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer one, 
 or perhaps a killer counterexample (e.g. when would an 
 interface-defined method be really bad?)

 Your thoughts welcome.

What happens when two interfaces implement the same method?

Ambiguity error.
 I thought 
 multiple subtyping without multiple inheritance was the raison d'Ítre 
 for interfaces.

Interfaces do foster multiple inheritance! Andrei
Feb 08 2010
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2/8/10 06:37, Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces from
 providing bodies for methods. I think that's a gratuitous limitation;
 the only distinguishing quality of an interface is that it has no state.
 Other than that, interfaces can always offer overridable functions that
 by default offer functionality in terms of existing interface functions.
 For example:

 interface Stack(T)
 {
 void push(T);
 void pop();
  property ref T top();
  property bool empty();
 T belowTop()
 {
 auto t = top;
 pop();
 auto result = top;
 push(t);
 }
 }

 The default implementation of belowTop does a fair amount of work. A
 particular implementation might just use that or override it with a more
 efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer one, or
 perhaps a killer counterexample (e.g. when would an interface-defined
 method be really bad?)

 Your thoughts welcome.


 Andrei

I only see two differences with abstract classes: interfaces can't have instance (and class?) variables and you can inherit from multiple interfaces. Am I missing something? Is this really necessary? Isn't abstract classes enough? Does this have similar problems (or the same) as multiple inheritance? /Jacob Carlborg
Feb 08 2010
next sibling parent reply Trass3r <un known.com> writes:
 I only see two differences with abstract classes: interfaces can't have  
 instance (and class?) variables and you can inherit from multiple  
 interfaces. Am I missing something? Is this really necessary? Isn't  
 abstract classes enough? Does this have similar problems (or the same)  
 as multiple inheritance?

Yeah, providing default functionality that can be overridden is exactly what abstract classes are for. Interfaces were introduced to circumvent all those multiple inheritance problems!
Feb 08 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Trass3r wrote:
 I only see two differences with abstract classes: interfaces can't 
 have instance (and class?) variables and you can inherit from multiple 
 interfaces. Am I missing something? Is this really necessary? Isn't 
 abstract classes enough? Does this have similar problems (or the same) 
 as multiple inheritance?

Yeah, providing default functionality that can be overridden is exactly what abstract classes are for.

Abstract classes are a kludge meant to put state in a partially implemented class. I don't think mentioning abstract classes is a counter-argument to defining methods to interfaces.
 Interfaces were introduced to circumvent all those multiple inheritance 
 problems!

Yes; a problem that abstract classes are not solving. Andrei
Feb 08 2010
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jacob Carlborg wrote:
 On 2/8/10 06:37, Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces from
 providing bodies for methods. I think that's a gratuitous limitation;
 the only distinguishing quality of an interface is that it has no state.
 Other than that, interfaces can always offer overridable functions that
 by default offer functionality in terms of existing interface functions.
 For example:

 interface Stack(T)
 {
 void push(T);
 void pop();
  property ref T top();
  property bool empty();
 T belowTop()
 {
 auto t = top;
 pop();
 auto result = top;
 push(t);
 }
 }

 The default implementation of belowTop does a fair amount of work. A
 particular implementation might just use that or override it with a more
 efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer one, or
 perhaps a killer counterexample (e.g. when would an interface-defined
 method be really bad?)

 Your thoughts welcome.


 Andrei

I only see two differences with abstract classes: interfaces can't have instance (and class?) variables and you can inherit from multiple interfaces. Am I missing something? Is this really necessary? Isn't abstract classes enough? Does this have similar problems (or the same) as multiple inheritance?

I think lack of state is indeed the only difference. The multiple inheritance bit makes all the difference, so I think abstract classes are not enough. A designer who wants to define some methods in an interface is forced at design time to choose an abstract class over an interface, thus severely limiting clients. Andrei
Feb 08 2010
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2/8/10 14:03, Andrei Alexandrescu wrote:
 Jacob Carlborg wrote:
 On 2/8/10 06:37, Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces from
 providing bodies for methods. I think that's a gratuitous limitation;
 the only distinguishing quality of an interface is that it has no state.
 Other than that, interfaces can always offer overridable functions that
 by default offer functionality in terms of existing interface functions.
 For example:

 interface Stack(T)
 {
 void push(T);
 void pop();
  property ref T top();
  property bool empty();
 T belowTop()
 {
 auto t = top;
 pop();
 auto result = top;
 push(t);
 }
 }

 The default implementation of belowTop does a fair amount of work. A
 particular implementation might just use that or override it with a more
 efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer one, or
 perhaps a killer counterexample (e.g. when would an interface-defined
 method be really bad?)

 Your thoughts welcome.


 Andrei

I only see two differences with abstract classes: interfaces can't have instance (and class?) variables and you can inherit from multiple interfaces. Am I missing something? Is this really necessary? Isn't abstract classes enough? Does this have similar problems (or the same) as multiple inheritance?

I think lack of state is indeed the only difference. The multiple inheritance bit makes all the difference, so I think abstract classes are not enough. A designer who wants to define some methods in an interface is forced at design time to choose an abstract class over an interface, thus severely limiting clients. Andrei

The obvious solution as I see it would have been using "regular" interfaces and template mixins. But that fails since the mixed in methods can't overload on the already present methods. Have traits (http://en.wikipedia.org/wiki/Trait_%28computer_science%29) ever been considered?
Feb 08 2010
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-02-08 11:58:53 -0500, retard <re tard.com.invalid> said:

 Mon, 08 Feb 2010 07:03:43 -0600, Andrei Alexandrescu wrote:
 
 I think lack of state is indeed the only difference. The multiple
 inheritance bit makes all the difference, so I think abstract classes
 are not enough. A designer who wants to define some methods in an
 interface is forced at design time to choose an abstract class over an
 interface, thus severely limiting clients.

I really wonder why you're doing this. NIH. Ever heard or Scala and traits? I'm sorry, but you didn't invent this feature - giving some kind of attribution would be honest. I can imagine how this proposal goes forward. Suddenly D 2 gets almost exactly the same feature (+ contracts) as Scala has had for a long time and somehow you get all the credit in the practical (C++/D) PL community.

Hum, where did Andrei claimed he invented this? To me who knows well Objective-C, this looks like an adaptation to D of the informal protocol concept, which was then superseded by optional methods in formal protocols in Objective-C 2.0. This pattern is used a lot in Objective-C, even though it's implemented differently and is more powerful due to categories. Also, I don't know much about SmallTalk, but given SmallTalk was the inspiration for Objective-C I wouldn't be surprised to see this there too. It's nice to give attribution, but where do we stop? Can you say you know for sure what was the real inspiration for this? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 08 2010
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2010-02-08 13:04:37 -0500, retard <re tard.com.invalid> said:

 No, I don't know the original source. Multiple inheritance is an old and
 widely known problem. I just meant that instead of bikeshedding here and
 reinventing everything from scratch, everyone interested in the topic
 should take a look at Scala's traits since traits basically are
 interfaces extended with function bodies. Scala also has a solution to
 the conflicting multiply inherited methods. I just mentioned Scala
 because it's also a C inspired object oriented language and this feature
 proposal is more or less 90% the same concept as traits, both
 syntactically and semantically.

From what I can read, traits in Scala are much than adding function definitions. You can add member variables and inherit from a base class. It acts much like an interface + a mixin. It's quite nice really. <http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5> -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 08 2010
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
retard wrote:
 Mon, 08 Feb 2010 07:03:43 -0600, Andrei Alexandrescu wrote:
 
 Jacob Carlborg wrote:
 On 2/8/10 06:37, Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces from
 providing bodies for methods. I think that's a gratuitous limitation;
 the only distinguishing quality of an interface is that it has no
 state. Other than that, interfaces can always offer overridable
 functions that by default offer functionality in terms of existing
 interface functions. For example:

 interface Stack(T)
 {
 void push(T);
 void pop();
  property ref T top();
  property bool empty();
 T belowTop()
 {
 auto t = top;
 pop();
 auto result = top;
 push(t);
 }
 }

 The default implementation of belowTop does a fair amount of work. A
 particular implementation might just use that or override it with a
 more efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer one,
 or perhaps a killer counterexample (e.g. when would an
 interface-defined method be really bad?)

 Your thoughts welcome.


 Andrei

instance (and class?) variables and you can inherit from multiple interfaces. Am I missing something? Is this really necessary? Isn't abstract classes enough? Does this have similar problems (or the same) as multiple inheritance?

inheritance bit makes all the difference, so I think abstract classes are not enough. A designer who wants to define some methods in an interface is forced at design time to choose an abstract class over an interface, thus severely limiting clients.

I really wonder why you're doing this. NIH. Ever heard or Scala and traits? I'm sorry, but you didn't invent this feature - giving some kind of attribution would be honest. I can imagine how this proposal goes forward. Suddenly D 2 gets almost exactly the same feature (+ contracts) as Scala has had for a long time and somehow you get all the credit in the practical (C++/D) PL community.

This one really takes the cake. Do you really believe I'm in this for snatching credit? Sheesh. I know of Scala's traits. They are different from overridable methods in interfaces, which are not nearly interesting enough to bring fame and fortune to anyone. Andrei
Feb 08 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
retard wrote:
 Mon, 08 Feb 2010 11:34:10 -0800, Andrei Alexandrescu wrote:
 
 I know of Scala's traits. They are different from overridable methods in
 interfaces, which are not nearly interesting enough to bring fame and
 fortune to anyone.

I apologize for being so rude.

That's a nice surprise (what about the backlog? :o)). Just a note, to me apologizing entails I plan to not repeat whatever it is I'm apologizing for.
 If I read the proposal correctly, traits 
 are its generalization:
 
 interface Stack(T)
 {
      void push(T);
      void pop();
       property ref T top();
       property bool empty();
      T belowTop()
      {
          auto t = top;
          pop();
          auto result = top;
          push(t);
          return result;
      }
 }
 
 vs
 
 trait Stack[T]
 {
      def push(t: T): Unit
      def pop: Unit
      def top: T // do not know how to port properties
      def empty: Boolean
      def belowTop: T
      {
          val t = top
          pop
          val result = top
          push(t)
          result
      }
 }
 
 But with these kind of features D's interfaces are getting closer and 
 closer to traits. What's missing? The linearization system, type members, 
 and member variables inside interfaces. OTOH Scala is lacking the 
 contract system.

Traits indeed offer more than interfaces. We're looking at sensible things to do within the time constraints we're having; traits would be a major effort, whereas methods in interfaces are just eliminating an artificial limitation. Traits are a possible addition to D3. Andrei
Feb 08 2010
parent reply grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 Traits indeed offer more than interfaces. We're looking at sensible 
 things to do within the time constraints we're having; traits would be a 
 major effort, whereas methods in interfaces are just eliminating an 
 artificial limitation. Traits are a possible addition to D3.

When will work on D3 be started? 6 months after your book has been published?
 Andrei

Feb 08 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Andrei Alexandrescu wrote:
 Traits indeed offer more than interfaces. We're looking at sensible 
 things to do within the time constraints we're having; traits would be 
 a major effort, whereas methods in interfaces are just eliminating an 
 artificial limitation. Traits are a possible addition to D3.

When will work on D3 be started? 6 months after your book has been published?

You may start it any time now. Andrei
Feb 08 2010
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
retard wrote:
 I really wonder why you're doing this. NIH. Ever heard or Scala and 
 traits? I'm sorry, but you didn't invent this feature - giving some kind 
 of attribution would be honest. I can imagine how this proposal goes 
 forward. Suddenly D 2 gets almost exactly the same feature (+ contracts) 
 as Scala has had for a long time and somehow you get all the credit in 
 the practical (C++/D) PL community.

Member functions with bodies is what C++ has with multiple inheritance. C++ multiple inheritance has been around a lot longer than Scala. D interfaces *are* C++ multiple interface classes, but with some restrictions (like no data members, no virtual base classes, and no function bodies). They are even implemented the same way, with vtables and thunks. This is nothing new.
Feb 08 2010
prev sibling next sibling parent retard <re tard.com.invalid> writes:
Mon, 08 Feb 2010 07:03:43 -0600, Andrei Alexandrescu wrote:

 Jacob Carlborg wrote:
 On 2/8/10 06:37, Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces from
 providing bodies for methods. I think that's a gratuitous limitation;
 the only distinguishing quality of an interface is that it has no
 state. Other than that, interfaces can always offer overridable
 functions that by default offer functionality in terms of existing
 interface functions. For example:

 interface Stack(T)
 {
 void push(T);
 void pop();
  property ref T top();
  property bool empty();
 T belowTop()
 {
 auto t = top;
 pop();
 auto result = top;
 push(t);
 }
 }

 The default implementation of belowTop does a fair amount of work. A
 particular implementation might just use that or override it with a
 more efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer one,
 or perhaps a killer counterexample (e.g. when would an
 interface-defined method be really bad?)

 Your thoughts welcome.


 Andrei

I only see two differences with abstract classes: interfaces can't have instance (and class?) variables and you can inherit from multiple interfaces. Am I missing something? Is this really necessary? Isn't abstract classes enough? Does this have similar problems (or the same) as multiple inheritance?

I think lack of state is indeed the only difference. The multiple inheritance bit makes all the difference, so I think abstract classes are not enough. A designer who wants to define some methods in an interface is forced at design time to choose an abstract class over an interface, thus severely limiting clients.

I really wonder why you're doing this. NIH. Ever heard or Scala and traits? I'm sorry, but you didn't invent this feature - giving some kind of attribution would be honest. I can imagine how this proposal goes forward. Suddenly D 2 gets almost exactly the same feature (+ contracts) as Scala has had for a long time and somehow you get all the credit in the practical (C++/D) PL community.
Feb 08 2010
prev sibling next sibling parent retard <re tard.com.invalid> writes:
Mon, 08 Feb 2010 12:59:33 -0500, Michel Fortin wrote:

 On 2010-02-08 11:58:53 -0500, retard <re tard.com.invalid> said:
 
 Mon, 08 Feb 2010 07:03:43 -0600, Andrei Alexandrescu wrote:
 
 I think lack of state is indeed the only difference. The multiple
 inheritance bit makes all the difference, so I think abstract classes
 are not enough. A designer who wants to define some methods in an
 interface is forced at design time to choose an abstract class over an
 interface, thus severely limiting clients.

I really wonder why you're doing this. NIH. Ever heard or Scala and traits? I'm sorry, but you didn't invent this feature - giving some kind of attribution would be honest. I can imagine how this proposal goes forward. Suddenly D 2 gets almost exactly the same feature (+ contracts) as Scala has had for a long time and somehow you get all the credit in the practical (C++/D) PL community.

Hum, where did Andrei claimed he invented this? To me who knows well Objective-C, this looks like an adaptation to D of the informal protocol concept, which was then superseded by optional methods in formal protocols in Objective-C 2.0. This pattern is used a lot in Objective-C, even though it's implemented differently and is more powerful due to categories. Also, I don't know much about SmallTalk, but given SmallTalk was the inspiration for Objective-C I wouldn't be surprised to see this there too. It's nice to give attribution, but where do we stop? Can you say you know for sure what was the real inspiration for this?

No, I don't know the original source. Multiple inheritance is an old and widely known problem. I just meant that instead of bikeshedding here and reinventing everything from scratch, everyone interested in the topic should take a look at Scala's traits since traits basically are interfaces extended with function bodies. Scala also has a solution to the conflicting multiply inherited methods. I just mentioned Scala because it's also a C inspired object oriented language and this feature proposal is more or less 90% the same concept as traits, both syntactically and semantically.
Feb 08 2010
prev sibling parent retard <re tard.com.invalid> writes:
Mon, 08 Feb 2010 11:34:10 -0800, Andrei Alexandrescu wrote:

 I know of Scala's traits. They are different from overridable methods in
 interfaces, which are not nearly interesting enough to bring fame and
 fortune to anyone.

I apologize for being so rude. If I read the proposal correctly, traits are its generalization: interface Stack(T) { void push(T); void pop(); property ref T top(); property bool empty(); T belowTop() { auto t = top; pop(); auto result = top; push(t); return result; } } vs trait Stack[T] { def push(t: T): Unit def pop: Unit def top: T // do not know how to port properties def empty: Boolean def belowTop: T { val t = top pop val result = top push(t) result } } But with these kind of features D's interfaces are getting closer and closer to traits. What's missing? The linearization system, type members, and member variables inside interfaces. OTOH Scala is lacking the contract system.
Feb 08 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 08 Feb 2010 00:37:53 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Walter has now implemented final methods in interfaces and also  
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces from  
 providing bodies for methods. I think that's a gratuitous limitation;  
 the only distinguishing quality of an interface is that it has no state.  
   Other than that, interfaces can always offer overridable functions  
 that by default offer functionality in terms of existing interface  
 functions. For example:

 interface Stack(T)
 {
      void push(T);
      void pop();
       property ref T top();
       property bool empty();
      T belowTop()
      {
          auto t = top;
          pop();
          auto result = top;
          push(t);
      }
 }

 The default implementation of belowTop does a fair amount of work. A  
 particular implementation might just use that or override it with a more  
 efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer one, or  
 perhaps a killer counterexample (e.g. when would an interface-defined  
 method be really bad?)

 Your thoughts welcome.

You forgot to return result :) Also, it should be implemented like this to prevent a problem where a stack with a single element is cleared before an exception is thrown. T belowTop() { auto t = top; pop(); scope(exit) push(t); return top; } As another example (not sure if it's "killer"), here is a more useful default method: T popTop() { scope(success) pop(); return top; } I can see real use for "give me the top, and pop it off at the same time." Now, if we make popTop final, it's 2 virtual function calls (popTop can be inlined), but if we make it overridable, the default implementation has 3 virtual calls, but can be optimized into 1 virtual call if so desired. I guess it's a tradeoff between whether you think most implementors will prefer to use some default implementation or most will want to optimize. But in the case you expect most to optimize, wouldn't you just provide no implementation? In the case of popTop, it's attractive to say "your stack class only needs to define these few primitives," but it's generally trivial to provide a popTop implementation that is more optimized. I've seen places where interface documentation says "implement this function like this: ..." which is quite annoying. final functions solve most of these, I'm not sure if we need another way to do it, but I can't see any reason why it should be disallowed. The judgement call of whether to provide an overridable implementation and no implementation would be difficult. I don't see a default-but-overridable implementation being used often. -Steve
Feb 08 2010
prev sibling next sibling parent reply Don <nospam nospam.com> writes:
Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also 
 contracts in interfaces, both of which I think are just awesome.
 
 We figured that essentially he artificially disallows interfaces from 
 providing bodies for methods. I think that's a gratuitous limitation; 
 the only distinguishing quality of an interface is that it has no state. 
  Other than that, interfaces can always offer overridable functions that 
 by default offer functionality in terms of existing interface functions. 
 For example:
 
 interface Stack(T)
 {
     void push(T);
     void pop();
      property ref T top();
      property bool empty();
     T belowTop()
     {
         auto t = top;
         pop();
         auto result = top;
         push(t);
     }
 }
 
 The default implementation of belowTop does a fair amount of work. A 
 particular implementation might just use that or override it with a more 
 efficient implementation.
 
 Many more examples can be imagined, but I'm looking for a killer one, or 
 perhaps a killer counterexample (e.g. when would an interface-defined 
 method be really bad?)
 
 Your thoughts welcome.
 
 
 Andrei

I don't understand this. How does belowTop() know how to call top()? It has the 'this' pointer, so that does have a pointer to the vtable; but since it doesn't know the inheritance hierarchy of the object which it applies to, how can it know which vtable index to use? With 'final' functions, of course there's no problem. It can be solved with thunks, of course, but I presume that's not the intention?
Feb 08 2010
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also 
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces from 
 providing bodies for methods. I think that's a gratuitous limitation; 
 the only distinguishing quality of an interface is that it has no 
 state.  Other than that, interfaces can always offer overridable 
 functions that by default offer functionality in terms of existing 
 interface functions. For example:

 interface Stack(T)
 {
     void push(T);
     void pop();
      property ref T top();
      property bool empty();
     T belowTop()
     {
         auto t = top;
         pop();
         auto result = top;
         push(t);
     }
 }

 The default implementation of belowTop does a fair amount of work. A 
 particular implementation might just use that or override it with a 
 more efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer one, 
 or perhaps a killer counterexample (e.g. when would an 
 interface-defined method be really bad?)

 Your thoughts welcome.


 Andrei

I don't understand this. How does belowTop() know how to call top()?

It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type. Andrei
Feb 08 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Don wrote:
 Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also 
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces 
 from providing bodies for methods. I think that's a gratuitous 
 limitation; the only distinguishing quality of an interface is that 
 it has no state.  Other than that, interfaces can always offer 
 overridable functions that by default offer functionality in terms 
 of existing interface functions. For example:

 interface Stack(T)
 {
     void push(T);
     void pop();
      property ref T top();
      property bool empty();
     T belowTop()
     {
         auto t = top;
         pop();
         auto result = top;
         push(t);
     }
 }

 The default implementation of belowTop does a fair amount of work. A 
 particular implementation might just use that or override it with a 
 more efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer 
 one, or perhaps a killer counterexample (e.g. when would an 
 interface-defined method be really bad?)

 Your thoughts welcome.


 Andrei


It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type.

Actually, I think Don has a point here. A virtual function (even on an interface) is always called with the 'this' pointer, not the interface pointer.

That is done via an adjustment of the reference. In the case of an interface method, no adjustment is necessary. Inside the method, "this" has the static type of the interface and the dynamic type whichever class implements the interface. Andrei
Feb 08 2010
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 15:13:33 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Don wrote:
  I don't understand this. How does belowTop() know how to call top()?

It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type.

an interface) is always called with the 'this' pointer, not the interface pointer.

That is done via an adjustment of the reference. In the case of an interface method, no adjustment is necessary. Inside the method, "this" has the static type of the interface and the dynamic type whichever class implements the interface.

void foo(Stack!T st) { auto x = st.belowTop(); } OK, so if st's virtual function for belowTop points to the default implementation, there is no adjustment. But what if the actual object *did* override the default implementation? Does it also receive the interface pointer as 'this'? Where does the adjustment happen? What happens if you have a reference to the actual concrete object type? Do you have to thunk to the correct interface to pass in the expected interface pointer? It can't be both ways.

If an object overrode the default implementation, the pointer to method belowTop will point to code that does do the adjustment. Andrei
Feb 08 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 16:09:19 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 15:13:33 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Don wrote:
  I don't understand this. How does belowTop() know how to call 
 top()?

It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type.

on an interface) is always called with the 'this' pointer, not the interface pointer.

That is done via an adjustment of the reference. In the case of an interface method, no adjustment is necessary. Inside the method, "this" has the static type of the interface and the dynamic type whichever class implements the interface.

{ auto x = st.belowTop(); } OK, so if st's virtual function for belowTop points to the default implementation, there is no adjustment. But what if the actual object *did* override the default implementation? Does it also receive the interface pointer as 'this'? Where does the adjustment happen? What happens if you have a reference to the actual concrete object type? Do you have to thunk to the correct interface to pass in the expected interface pointer? It can't be both ways.

If an object overrode the default implementation, the pointer to method belowTop will point to code that does do the adjustment.

If I understand this correctly, calling such a "default implementation" function is different than calling a standard interface function. And each entry in such an interface for a overridden method will point to a "pre function" that adjusts the 'this' reference before jumping to the real implementation.

Actually that's what's happening today.
 The vtable entries of the object itself would point to a function that 
 does not do the adjustment, correct?

Yes, but let's not forget that each object stores more than one vptrs.
 I think all the information is available to make this work, the only 
 issue I see is that a function with a default implementation 
 artificially changes the ABI for that function.  Adding a default 
 implementation therefore will make compiled objects incompatible, even 
 with the same vtable layout.

Not getting this, but I'll let Walter weigh in. Andrei
Feb 08 2010
prev sibling parent reply Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 15:13:33 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Don wrote:
  I don't understand this. How does belowTop() know how to call top()?

It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type.

an interface) is always called with the 'this' pointer, not the interface pointer.

That is done via an adjustment of the reference. In the case of an interface method, no adjustment is necessary. Inside the method, "this" has the static type of the interface and the dynamic type whichever class implements the interface.

void foo(Stack!T st) { auto x = st.belowTop(); } OK, so if st's virtual function for belowTop points to the default implementation, there is no adjustment. But what if the actual object *did* override the default implementation? Does it also receive the interface pointer as 'this'? Where does the adjustment happen? What happens if you have a reference to the actual concrete object type? Do you have to thunk to the correct interface to pass in the expected interface pointer? It can't be both ways. -Steve

I have a strong suspicion that it's exactly the same as multiple inheritance. I thought about this quite a bit a year ago, and initially I thought that having no data members made it OK. But it's deceptive. Virtual member functions are still a problem. I think you can only have final functions.
Feb 08 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Don wrote:
 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 15:13:33 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Don wrote:
  I don't understand this. How does belowTop() know how to call top()?

It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type.

on an interface) is always called with the 'this' pointer, not the interface pointer.

That is done via an adjustment of the reference. In the case of an interface method, no adjustment is necessary. Inside the method, "this" has the static type of the interface and the dynamic type whichever class implements the interface.

void foo(Stack!T st) { auto x = st.belowTop(); } OK, so if st's virtual function for belowTop points to the default implementation, there is no adjustment. But what if the actual object *did* override the default implementation? Does it also receive the interface pointer as 'this'? Where does the adjustment happen? What happens if you have a reference to the actual concrete object type? Do you have to thunk to the correct interface to pass in the expected interface pointer? It can't be both ways. -Steve

I have a strong suspicion that it's exactly the same as multiple inheritance. I thought about this quite a bit a year ago, and initially I thought that having no data members made it OK. But it's deceptive. Virtual member functions are still a problem. I think you can only have final functions.

I think it's only data. The problem is that you end up storing multiple copies of the same interface object inside the final object. As long as the per-interface state is compiler-maintained and immutable, you should be in good shape. Andrei
Feb 08 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Don wrote:
 Andrei Alexandrescu wrote:
 Walter has now implemented final methods in interfaces and also  
 contracts in interfaces, both of which I think are just awesome.

 We figured that essentially he artificially disallows interfaces from  
 providing bodies for methods. I think that's a gratuitous limitation;  
 the only distinguishing quality of an interface is that it has no  
 state.  Other than that, interfaces can always offer overridable  
 functions that by default offer functionality in terms of existing  
 interface functions. For example:

 interface Stack(T)
 {
     void push(T);
     void pop();
      property ref T top();
      property bool empty();
     T belowTop()
     {
         auto t = top;
         pop();
         auto result = top;
         push(t);
     }
 }

 The default implementation of belowTop does a fair amount of work. A  
 particular implementation might just use that or override it with a  
 more efficient implementation.

 Many more examples can be imagined, but I'm looking for a killer one,  
 or perhaps a killer counterexample (e.g. when would an  
 interface-defined method be really bad?)

 Your thoughts welcome.


 Andrei


It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type.

Actually, I think Don has a point here. A virtual function (even on an interface) is always called with the 'this' pointer, not the interface pointer. A real example: interface A { int bar(); int foo() { return bar(); } } class C : A { override int bar() { return 1;} } class D : C { override int foo() { return 2;} } if you have a reference to A, when calling foo, what do you pass as the this pointer? I think the way it works is an interface call does an interface lookup to get the 'true' this pointer (this is a quick lookup since an interface pointer has the offset info at a predetermined location), so the object pointer is passed into foo. When the actual type is a D object, a D reference is expected, but when the actual type is a C object, what is expected? The compiler cannot tell what the underlying type is, so when the actual type is a C object, a C reference will be passed in. So in a "default implementation", there has to be an implicit thunk to convert the type back into an interface. Basically, the function foo as implemented in A looks like this: int foo() { A __this = cast(A)this; // do a thunk, 'this' is of type Object return __this.bar(this); // no lookup of this required, so this is the same as a standard virtual call } This severely lowers my taste for this idea. I think a thunk uses a linear lookup of the interface list at runtime to find the correct interface. A way you might be able to get rid of this problem is to compile the default implementation as if it were a function of the class that implements the interface. I could probably live with that, but this feature seems more complicated than it is worth. -Steve
Feb 08 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 08 Feb 2010 15:13:33 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Don wrote:
  I don't understand this. How does belowTop() know how to call top()?

It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type.

an interface) is always called with the 'this' pointer, not the interface pointer.

That is done via an adjustment of the reference. In the case of an interface method, no adjustment is necessary. Inside the method, "this" has the static type of the interface and the dynamic type whichever class implements the interface.

void foo(Stack!T st) { auto x = st.belowTop(); } OK, so if st's virtual function for belowTop points to the default implementation, there is no adjustment. But what if the actual object *did* override the default implementation? Does it also receive the interface pointer as 'this'? Where does the adjustment happen? What happens if you have a reference to the actual concrete object type? Do you have to thunk to the correct interface to pass in the expected interface pointer? It can't be both ways. -Steve
Feb 08 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 08 Feb 2010 16:09:19 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 15:13:33 -0500, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Don wrote:
  I don't understand this. How does belowTop() know how to call  
 top()?

It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type.

an interface) is always called with the 'this' pointer, not the interface pointer.

That is done via an adjustment of the reference. In the case of an interface method, no adjustment is necessary. Inside the method, "this" has the static type of the interface and the dynamic type whichever class implements the interface.

{ auto x = st.belowTop(); } OK, so if st's virtual function for belowTop points to the default implementation, there is no adjustment. But what if the actual object *did* override the default implementation? Does it also receive the interface pointer as 'this'? Where does the adjustment happen? What happens if you have a reference to the actual concrete object type? Do you have to thunk to the correct interface to pass in the expected interface pointer? It can't be both ways.

If an object overrode the default implementation, the pointer to method belowTop will point to code that does do the adjustment.

If I understand this correctly, calling such a "default implementation" function is different than calling a standard interface function. And each entry in such an interface for a overridden method will point to a "pre function" that adjusts the 'this' reference before jumping to the real implementation. The vtable entries of the object itself would point to a function that does not do the adjustment, correct? I think all the information is available to make this work, the only issue I see is that a function with a default implementation artificially changes the ABI for that function. Adding a default implementation therefore will make compiled objects incompatible, even with the same vtable layout. As of today, I don't see this being a problem, since you generally only build static D programs. But I can't see a huge flaw in the idea. -Steve
Feb 08 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 08 Feb 2010 17:19:28 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 16:09:19 -0500, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 15:13:33 -0500, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Mon, 08 Feb 2010 14:36:37 -0500, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Don wrote:
  I don't understand this. How does belowTop() know how to call  
 top()?

It calls top() through the normal interface mechanism. Inside belowTop(), this has Stack!T type.

on an interface) is always called with the 'this' pointer, not the interface pointer.

That is done via an adjustment of the reference. In the case of an interface method, no adjustment is necessary. Inside the method, "this" has the static type of the interface and the dynamic type whichever class implements the interface.

{ auto x = st.belowTop(); } OK, so if st's virtual function for belowTop points to the default implementation, there is no adjustment. But what if the actual object *did* override the default implementation? Does it also receive the interface pointer as 'this'? Where does the adjustment happen? What happens if you have a reference to the actual concrete object type? Do you have to thunk to the correct interface to pass in the expected interface pointer? It can't be both ways.

If an object overrode the default implementation, the pointer to method belowTop will point to code that does do the adjustment.

implementation" function is different than calling a standard interface function. And each entry in such an interface for a overridden method will point to a "pre function" that adjusts the 'this' reference before jumping to the real implementation.

Actually that's what's happening today.

Yes, but I think it's happening at the call site and not inside the function itself. I will run a test... You are right, I was wrong. So this is already the way it works (good to know!): interface I { void foo(); } interface J { void foo(); } class C : I, J { int i; void foo() { i = 5;} } produces the following asm for C.foo: _D13testinterface1C3fooMFZv: push EBP mov EBP,ESP sub ESP,4 mov dword ptr 8[EAX],5 leave ret nop _TMP0: add EAX,0FFFFFFF4h jmp near ptr _D13testinterface1C3fooMFZv _TMP1: add EAX,0FFFFFFF0h jmp near ptr _D13testinterface1C3fooMFZv where _TMP0 and _TMP1 are the little pre functions that get stored in the interface vtables. I therefore don't think there are any issues, I misunderstood the way interface functions work. I didn't realize the interface function called a little pre function. I thought it was done at the call site before the call.
 The vtable entries of the object itself would point to a function that  
 does not do the adjustment, correct?

Yes, but let's not forget that each object stores more than one vptrs.

Right, I just thought the different vtables contained identical references to the same functions. It makes sense that is not the case.
 I think all the information is available to make this work, the only  
 issue I see is that a function with a default implementation  
 artificially changes the ABI for that function.  Adding a default  
 implementation therefore will make compiled objects incompatible, even  
 with the same vtable layout.

Not getting this, but I'll let Walter weigh in.

It's my bad, I thought normal interface calls were different than this method of having a little "pre" function, but it makes sense to do it that way. Sorry for the noise. I retract my objection on these grounds :) -Steve
Feb 08 2010
prev sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Walter has now implemented final methods in interfaces and also
 contracts in interfaces, both of which I think are just awesome.
 We figured that essentially he artificially disallows interfaces from
 providing bodies for methods. I think that's a gratuitous limitation;
 the only distinguishing quality of an interface is that it has no state.
   Other than that, interfaces can always offer overridable functions
 that by default offer functionality in terms of existing interface
 functions. For example:
 interface Stack(T)
 {
      void push(T);
      void pop();
       property ref T top();
       property bool empty();
      T belowTop()
      {
          auto t = top;
          pop();
          auto result = top;
          push(t);
      }
 }
 The default implementation of belowTop does a fair amount of work. A
 particular implementation might just use that or override it with a more
 efficient implementation.
 Many more examples can be imagined, but I'm looking for a killer one, or
 perhaps a killer counterexample (e.g. when would an interface-defined
 method be really bad?)
 Your thoughts welcome.
 Andrei

If we're going to do this, then why not allow (biggest oxymoron of all time) concrete interfaces, i.e. interfaces that can be instantiated? This would be useful for things like the template method pattern. Example: /**Reduction implemented by template method pattern.*/ interface Reduce(T, U) { T opCall(T initialValue, U[] array) { T result = initialValue; foreach(elem; array) { result = reductionFunction(result, elem); } return result; } abstract T reductionFunction(T, U); } interface Sum(T, U) : Reduce!(T, U) { override T reductionFunction(T lhs, U rhs) { return lhs + rhs; } } An instantiation of an interface wouldn't perform any heap allocations and would just return a pointer to the relevant vtable. It would then work just like an interface inherited from a class: Calling a method dispatches based on the vtable logic. The this pointer is simply a pointer to the relevant vtable. With the this pointer in hand, the function can call other virtual functions just like a regular class.
Feb 08 2010