On Fri, Sep 5, 2014 at 1:07 PM, Nick Wellnhofer (JIRA) <[email protected]> wrote:
>
> Nick Wellnhofer commented on CLOWNFISH-12:
> ------------------------------------------
>
> Another approach are "implicit" interfaces like in Go. Some details how to
> implement this can be found here:
>
>     http://research.swtch.com/interfaces
>
> We'd have to add method signatures to our runtime class metadata. Then the
> creation of an interface instance from an object would typically require a
> heap allocation and a hash lookup. Another downside is that interface
> instances must be decref'd. But Go-like interfaces would be a really cool
> feature.

Implicitly satisfied interfaces are a very nice feature of Go, and I would
love to have them in Clownfish.

Building the interface table on the fly at runtime seems straightforward:

    In our simple example, the method table for Stringer has one method, while
    the table for Binary has two methods. In general there might be _ni_
    methods for the interface type and _nt_ methods for the concrete type. The
    obvious search to find the mapping from interface methods to concrete
    methods would take `O(ni × nt)` time, but we can do better. By sorting the
    two method tables and walking them simultaneously, we can build the
    mapping in `O(ni + nt)` time instead.

However, we have a problem with C implementation code: how can we get the C
compiler to tell us whether invoking an interface method on a given value is
safe?

In Go, the compiler detects invalid assignment to an interface variable
(<http://play.golang.org/p/mT743SWoxu>):

    package main

    type Futzer interface {
        Futz()
    }

    func main() {
        var futzer Futzer = "I am not a Futzer" // fails to compile.
        futzer.Futz()
    }

In Clownfish-flavored C, we can perform a runtime check:

    // Frobber implements Futzer, so the runtime check succeeds.
    Frobber *frobber = Frober_new();
    Futzer  *futzer  = (Futzer*)IMPLEMENTS(frobber, iFUTZER);  // succeeds
    Frober_Futz(futzer);

    // String doesn't implement Futzer, so the runtime check fails.
    char   *cstring = strdup("I am not a Futzer")
    String *string  = Str_new_from_utf8(cstring, strlen(cstring));
    Futzer *futzer  = (Futzer*)IMPLEMENTS(string, iFUTZER);  // runtime error
    Futzer_Futz(futzer);

I think we can make that check redundant using DSO-style lazy loading
techniques: have each class start off with a dummy interface table populated
with stubs which lazily build the interface table, replacing themselves and
reinvoking on success or throwing an exception on failure.

However, unless we cast or have interface method invocation functions take
`void*` for the invocant, they'll produce compile-time warnings.

    // Futzer_Futz() takes `Futzer*`: false compile-time warning.
    Futzer_Futz(frobber);

    // Futzer_Futz() takes `Futzer*`: compile-time warning, segfault.
    Futzer_Futz(cstring);

    // Futzer_Futz() takes `void*`: no compile-time warning, runtime error.
    Futzer_Futz(string);

    // Futzer_Futz() takes `void*`: no compile-time warning, segfault.
    Futzer_Futz(cstring);

Marvin Humphrey

Reply via email to