On Sep 15, 2014, at 18:14 , Marvin Humphrey <[email protected]> wrote:

> On Mon, Sep 15, 2014 at 3:01 AM, Nick Wellnhofer <[email protected]> wrote:
> 
>> Originally, I didn't think of it as a cast operation but more like a
>> constructor for interface objects. It would be nice if the API would support
>> an implementation using interface objects that are allocated separately.
> 
> That's possible under the current proposal by combining an INCREF and a cast,
> right?
> 
> Let's also consider the possibility of representing interface objects using a
> two-word struct, like Go.

My original idea was to allocate interface objects on the heap and use a 
separate refcount. In this case, they should start with a refcount of 1. But 
then we’d either need separate INCREF/DECREF macros for interfaces which would 
mean a different API anyway and would be error-prone. Alternatively, it should 
be possible to make interface objects inherit from Obj, using the class vtable 
as itable (or at least making INCREF and DECREF compatible). With interfaces, 
Obj basically only needs the Inc_RefCount, Dec_RefCount and Destroy methods. 
Interface objects would then essentially be proxy objects that forward all 
method calls (expect for memory management) to the wrapped object.

But it’s also possible to allocate interface structs on the stack, pass them by 
value and reuse the original object’s refcount. Refcount handling would then be 
more cumbersome and error-prone, though.

> This more or less works for invocations, but also means that you can't use the
> interface object in any place we would ordinarily use an `Obj*`.
> 
>    int
>    S_compare(void *va, void *vb) {
>        Comparer *a = *(Comparer*)va;
>        Comparer *b = *(Comparer*)vb;
>        return Comparer_Compare_To(*a, b->obj);  // complicated.
>    }

That’s true in this case but I don’t think that’s a typical example.

> So, the only benefit is slightly streamlined interface method dispatch, but it
> comes at the cost of making the C API for using interface objects more
> cumbersome.

IMO, the main benefit is fast method dispatch without the need for the possibly 
huge itables arrays. The main downside is that the creation of interface 
objects is expensive. Using the same pointer for objects and interface objects 
has many benefits but it means that we have to find the correct method pointer 
on every method invocation which is either slow or memory-hungry.

> It also makes it more difficult to convert to host objects and back.  For
> instance, we can't store a two-word struct in a Perl SV's IV slot.

Yeah, but only for stack-allocated structs. It shouldn’t be necessary to expose 
interface objects to duck typing languages anyway. Instead of an interface 
object, we should simply pass the original object. Name-based method dispatch 
should then do the right thing when calling an interface method. Conversion of 
host language objects to interface objects should also happen behind the 
scenes. We don’t want to force host language code to cast objects to interfaces 
manually.

> That doesn't seem worthwhile.  It might be different if the compiler were able
> to perform conversions implicitly as with Go's compiler, but even then
> interoperability would suffer -- and Clownfish is all about interoperability.

I wouldn’t rule out this approach so quickly. We should also still consider 
“traditional” interfaces where every class must declare the interfaces it 
implements explicity. This would make both the cast to interface types and 
method dispatch extremely cheap.

Nick

Reply via email to