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
