Implementing an interface for an existing type

I’ve thought about this potential feature a lot lately. By the way, I am not aware of any language supporting this(*), so if you do know one, please let me know. I’d be interested to see how it is done.

Okay, so here’s the thing:
Imagine you have some type Foo, and you don’t have access to that type. That may be because it is part of the StdLib or an external library, or you’re just not allowed to change that type for some reason. But that type Foo could conceptually implement the interface Bar, it’s just that it wasn’t done by whoever wrote Foo. With the help of a little adapter code, you could make Foo implement Bar. Except you can’t, because that’s not a supported feature of the Kotlin programming language (or any other language I am aware of).

If it was a feature, it could look like this, for example:

[public] implement Foo : Bar {
     override fun someFunctionFromBar(): Int {
          // 'this' refers to an instance of Foo
    }
    // ...
}

I’m really not sure what the syntax should be here, but I think you get the point.

Now, why would this be useful? I got one example for you:

Imagine you want to write a library that provides a lot of common core ML algorithms (for example, a library for categorical encoding, for distance measures, for normalization or whatever). Now, the coolest thing would be if that library was completely framework-agnostic, meaning that you can just use it in combination with the DS/ML/DL framework of your choice. Now, those frameworks might all have their own classes to model “data records”. Maybe they’re wrapping around regular arrays, maybe they’re using some kind of ndarray implementation or something completely else. But all of their classes to model such data records are conceptually the same, although it is very unlikely that they will implement a common interface.

Let’s suppose there would be such an interface, though. Then, you could easily just use that interface as your access point in your library to implement all your algorithms. For example, you could implement distance measures only relying on that common interface (let’s call it DataRecord):

fun euclidean(): (DataRecord, DataRecord) -> Double = { p, q ->
    val dim = requireSameDimension(p, q)
    var sum = 0.0
    for (i in 0 until dim) {
        sum += sqr(p[i] - q[i])
    }
    sqrt(sum)
}

Now we know that this convenient interface does not exist, so let’s just write our own, in which we expose any functionality a common data record should have. Now with the ability to implement interfaces for existing types, we could write little adapters for all major frameworks, and tell the users of the library that they can even write their own adapter if their framework of choice is not supported.

I think you get the point of this. There are a lot of other applications for this feature, so if you have an idea of another useful application, let me know.

So, what do you think? Should this be a language feature? Would you use it?

(*) - I don’t know enough about Rust to know whether impl Trait for Type would actually work the way I describe this feature.

This is honestly quite fascinating how this ideas just pops up into everyone’s mind. This is what is known in functional programming as Type Classes, and yes they do exactly what you’re saying. They were proposed before for Kotlin with KEEP-87, but ultimately the Kotlin team decided on adding multiple receivers instead using a concept called decorators. And so basically, this feature will be achievable in 1.5 but using a receiver instead. So the way to define your Bar interface for Foo would be by first defining Bar as this:

interface Bar<A> {
    fun A.someFunctionFromBar(): Int
}
object FooBar: Bar<Foo> {
    override fun Foo.someFunctionFromBar(): Int = Foo.someProp + 42
}

// Here's a function that needs something implementing Bar
@with<Bar<A>>
fun <A> useBar(a: A): String {
    return a.someFunctionFromBar().toString()
}

// and the use site
fun main() {
    with(FooBar) {
        println(useBar(Foo(someProp = 0)))
    }
}
6 Likes

Also check out the section on Type Classes/Extension Interfaces on KT-10468

This is very interesting! I was following the “multiple receivers” / decorator discussion for some time. I also heard Roman Elizarov elaborating it during the Kotlin Online Event. However I never thought about it this way, that you could essentially implement interfaces for existing types using multiple receivers and decorators.

I’m still not quite sure about the decorator syntax, it kinda reminds me of Java’s annotation hell. But the feature in itself is undoubtedly very useful!

1 Like

I do see the resemblance that it has with annotations, but remember that this is basically syntatic sugar over a wrapper function, so you can actually compose decorators and define a decorator that applies like 50 different decorators to your function and then you only have to put that one decorator. They’re also different from annotations in that you can just Ctrl-Click and see their source. The same syntax is used in Python, btw, and people seem to be fine with it there. Tbh I don’t necessarily adore the decorator syntax but I can’t really see any other syntax to define it with that won’t add extra unnecessary noise (for example, the issue did hint at the possibility of a scope-like decorator definition where you have ig something like decorated : //Decorators go here { // functions go here }
Note: this isn’t what anyone suggested at all but it’s what I imagine it to be. And honestly I really don’t admire that because it just adds an extra level of nesting and noise, so unless there’s a syntax that’s better than the annotation-like one I guess we just have to stick with it

1 Like

Yeah, I suppose the general idea of this syntax is fine, I’m just not sure about using the same character @ as you would use for annotations. Just the camelCase / PascalCase convention is, in my opinion, not enough to discriminate between these two very different features. It makes me think of C++, where a single operator, like &, can have many different meanings depending on the context. This heavily impacts code readability and understandability. What if we’d just have a # instead of an @ for decorators, for example (a bit like the rust syntax). I mean, it doesn’t have to be exactly this character, but atleast something other than @ to prevent the confusion, you know what I mean?

1 Like

Hmm, yeah I perfectly understand what you’re getting at. I think maybe @ is used just for familiarity but to be honest I wouldn’t mind a # or any other characters being used for that just as long as the feature itself doesn’t require a lot of mental gymnastics to use.

Go supports that. Actually that’s the only way of doing interfaces. Go by Example: Interfaces

1 Like

In my opinion this is the key difference to why annotations often feel like “annotation hell” in java.

In java, annotations are most often handled in a dynamic way via reflection, which feels like ungraspable magic.

The new Kotlin decorators on the other hand will be resolved statically. You will always know the actual implementation of a decorator at compile time.

A similar feature is proposed for JavaScript and already available in Typescripts. And it is called decorator their, too. And they resolve statically, too, which is quite ironic considering the dynamic nature of JavaScript.

2 Likes

This sounds a lot like structural typing to me, as opposed to the nominal typing Kotlin and Java support.

The Manifold framework provides structural typing for Java using Structural Interfaces. The section on Implementation by Extension is particularly relevant to the OP.

The framework even lets a Map satisfy a structural interface as seen here.

1 Like

impl Trait for Type does work in this way in Rust. You could implement traits you own on types you don’t own, or implement traits you don’t own on types you own, but you couldn’t implement traits you don’t own on types you don’t own, known as the “orphan rule”. Without this rule, there’s nothing stopping two rival libraries implementing the same trait on the same type using different implementations. If both libraries are imported to the same application, which implementation should the compiler use? A library is capable of breaking another library’s code!

I think you might run into a similar problem. Two conflicting “adapter” libraries would break each other, so you’d need to introduce a bunch of safeguards, both in the code and also in the ecosystem of providing these adapters.

Remember you can always wrap the foreign type inside a data class Wrapper(x: Type) and implement whatever interfaces you like on that. The same is true for Rust: struct Wrapper(Type);.

A very similar language that supports this too is Swift, through its version of extensions (note that interfaces are called protocols in Swift): Protocols — The Swift Programming Language (Swift 5.7)

extension Foo : Bar {
    func someFunctionFromBar(): {
        //implement
    }
}

I feel like this would have been the most natural way to implement it, but of course the pesky JVM won’t allow it. :slight_smile:

1 Like

Interesting way to solve this problem, but quite limiting.

Another way to solve the problem would be to make an implementation available only when it is imported or explicitly declared to be used. This way you could even provide multiple implementations for various use cases your own.

If I am not mistaken, the type classes keep for Kotlin was designing a way of declaring the implementation, combined with some way of implicitly doing it.

3 Likes

I’m not sure sure how how viable it is to implement this in Kotlin, but I can tell you that can be done in CLOS, which is the object framework in Common Lisp.

It can be done in Lisp because object framework is programmable from within the runtime. As far as I know the necessary building blocks simply doesn’t exist in the JVM. Instead, one has to use tricks like delegation to emulate such behaviour.

Just wanted to clarify that the feature is still in the early stages of design, and thus will not make it into 1.5.

1 Like

Oh whoops I think I made the mistake of thinking that “Prototype multiple receivers” means finalising the design, but now that I think about it of course it isn’t :man_facepalming: . Just to clarify, though, there will be at least an experimental version of multiple receivers in 1.5, right? Kind of like in a similar fashion to how contracts are currently experimental?

1 Like