Declaring external extension members

I want to add an extension member to a class, but I believe this is currently impossible.
example:

class Foo {
    fun String.foo() {}
}

fun Foo.String.bar() {} // hypothetical syntax

fun doFoo(fn: Foo.() -> Unit) { /* ... */ }

fun main() {
    doFoo {
        "asdf".foo()
        "asdf".bar()
    }
}

I found this limitation when I was using the HTML DSL and wanted to add additional functions to String.

It is called multiple receivers and discussed in KEEP-176. I personally dislike dot syntax proposed by @chuckjaz and prefer bracket notation [Foo, String].bar(). The proposal has a lot of potential and covers most of type-class uses. Now all we need is to wait until new compiler is out (so there are some free kotlin language developers) and persuade @abreslav to consider it.

1 Like

I don’t want to add the extension to Foo and String, I want to add the extension as an extension to String as a member of Foo.
https://kotlinlang.org/docs/reference/extensions.html#declaring-extensions-as-members

It is exactly the same. Member extension has two receivers: member receiver and extension receiver. The idea of multiple receivers is to detach what was previously member receiver from actual member. If you had a function fun [Foo, String].bar(), you can call it with with(foo){ "ddd".bar() } since you have two receivers - one provided by with function and one provided explicitly.

There are some details though. Multiple receivers could be implemented in different ways. The order could matter (break backward compatibility in some cases) or it could not (it is in my final proposal and it also breaks some rare corner cases).

Working with the kotlinx-html library was also the first time when I direly needed this feature.

@darksnake is right, it is called multiple receivers. And the keep he linked to is what you need to wait for.

That’s more or less the multiple receivers. Multiple receivers does not mean that the extension is added to String and Foo separately. The extension cannot be called when only one of String or Foo is available in the context. Both must be available.

Ah I see, thanks.