February 08, 2010
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.
>>  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.


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
February 08, 2010
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
February 08, 2010
== 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.
February 08, 2010
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
February 08, 2010
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.
>>>  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.
> 
> 
> 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
February 08, 2010
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.
February 08, 2010
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.
>>>  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.
> 
> 
> 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.
February 08, 2010
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.
>>>>  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.
>>   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.

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
February 08, 2010
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.
>>>>  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.
>>
>>
>> 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
February 08, 2010
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.
>>>>>  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.
>>>   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.
> 
> 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