# Extensions and backward compatibility

**URL:** <https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949>\
**Category:** Language Design\
**Created:** [August 28, 2020, 5:26am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949 "2020-08-28T05:26:38Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![wdoker](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/wdoker/32/4748_2.png) [@wdoker](https://discuss.kotlinlang.org/u/wdoker)\
**Post date:** [August 28, 2020, 5:26am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/1 "2020-08-28T05:26:38Z")

</div>

Kotlin [documentation](https://kotlinlang.org/docs/reference/extensions.html#extensions-are-resolved-statically):

> If a class has a member function, and an extension function is defined which has the same receiver type, the same name, and is applicable to given arguments, the member always wins.

Suppose you are a library author, and you want to maintain strict backward compatibility of your API.  
How do you solve this problem:

```auto
// V0
class PublicApi {
	// ...
}

// V1
class PublicApi {
	// add new public member with implementation
	fun foo() {
		// ...
	}
}

// User code using V0
fun PublicApi.foo() {
	// ...
}

```

User code **silently** breaks in V1 because of different implementation of extension `foo` and member `foo`.

Does this mean that you cannot add new members to the class once you’ve published it?

---

<div class="post-metadata">

**Author:** ![kyay10](https://avatars.discourse-cdn.com/v4/letter/k/c57346/32.png) [@kyay10](https://discuss.kotlinlang.org/u/kyay10)\
**Post date:** [August 28, 2020, 8:34am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/2 "2020-08-28T08:34:50Z")

</div>

Internally, Kotlin has an [Annotation](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/internal/Annotations.kt#L27) that does exactly that. From the comment on that annotation:

> Specifies that a corresponding member has the lowest priority in overload resolution.

It is internal though, but there is a small hack to be able to use it. Please refer to [this](https://discuss.kotlinlang.org/t/why-is-inlineonly-internal/12441) post on how to exactly do it. It should only take you about 2 minutes lol.

---

<div class="post-metadata">

**Author:** ![Wasabi375](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/wasabi375/32/4741_2.png) [@Wasabi375](https://discuss.kotlinlang.org/u/Wasabi375)\
**Post date:** [August 28, 2020, 9:00am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/3 "2020-08-28T09:00:22Z")

</div>

This is not that bad a change than you might think, it will only break code when it is recompiled. Because extension functions are resolved statically this won’t interfere with code that is compiled against an older version of your library.

One solution to the problem would be to only add new member functions in bigger releases (only 1.x insteaad of 1.x.y releases) and then properly document that these functions have been added in the change notes.  
Also there is a compiler warning on the extension function, that it shadows an existing member function. Ok, there are projects out there that ignore compiler warnings, but I that’s nothing you can change.

---

<div class="post-metadata">

**Author:** ![darksnake](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/darksnake/32/2479_2.png) [@darksnake](https://discuss.kotlinlang.org/u/darksnake)\
**Post date:** [August 28, 2020, 9:12am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/4 "2020-08-28T09:12:49Z")

</div>

And if you compile a new version, you will get a warning.

---

<div class="post-metadata">

**Author:** ![wdoker](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/wdoker/32/4748_2.png) [@wdoker](https://discuss.kotlinlang.org/u/wdoker)\
**Post date:** [August 28, 2020, 9:48am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/5 "2020-08-28T09:48:26Z")

</div>

> [@kyay10](#):
>
> Internally, Kotlin has an [Annotation](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/internal/Annotations.kt#L27) that does exactly that.

Unfortunately, even if that annotation goes public someday, it will not solve the problem. `HideMembers` increase priority of the marked function (if i correctly understand the docs). That means every time you use 3-rd party API and create extensions for it, you also need to mark every extension with `HideMembers`, which does not seem practical.

If we are talking about magic annotations, then what we really need is an annotation that does exactly opposite - reduces overload priority, so that i can mark my new API members and don’t worry about user code breaking suddenly. But it seems like a hack anyway.

The only real solution came to my mind is compile-time **error** on extension/member conflict which will break existing code.

So, ironically, potential backward compatibility issues can not be fully resolved because of backward compatibility.

---

<div class="post-metadata">

**Author:** ![fatjoe79](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/fatjoe79/32/4221_2.png) [@fatjoe79](https://discuss.kotlinlang.org/u/fatjoe79)\
**Post date:** [August 28, 2020, 9:55am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/6 "2020-08-28T09:55:29Z")

</div>

> [@wdoker](#):
>
> The only real solution came to my mind is compile-time **error** on extension/member conflict which will break existing code.

It would only break existing code when it is re-compiled. Already compiled code will continue to use the extension function as stated in a previous post.

A compiler flag would be nice that turns the warnings into errors.

---

<div class="post-metadata">

**Author:** ![wdoker](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/wdoker/32/4748_2.png) [@wdoker](https://discuss.kotlinlang.org/u/wdoker)\
**Post date:** [August 28, 2020, 10:09am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/7 "2020-08-28T10:09:51Z")

</div>

Yes, i know we are talking about source-level compatibility, not binary.

And yes, compiler flag might be a solution if we could force it on in all CI pipelines, but we can’t, and we can’t force people to not ignore warnings.

In my own pipeline, I have prohibited a build with warnings a long time ago. Now people are throwing `@Suppress` everywhere 😀

---

<div class="post-metadata">

**Author:** ![Wasabi375](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/wasabi375/32/4741_2.png) [@Wasabi375](https://discuss.kotlinlang.org/u/Wasabi375)\
**Post date:** [August 28, 2020, 10:28am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/8 "2020-08-28T10:28:35Z")

</div>

> [@wdoker](#):
>
> In my own pipeline, I have prohibited a build with warnings a long time ago. Now people are throwing `@Suppress` everywhere 😀

I do that myself, but I think this is a good thing. A warning means, “hey, this part of your code as a high likelihood of creating errors”. I think of suppress like “Yes, I know and I checked this piece of code, it’s fine here, but please let me know of any other issues you might find”. I mainly use suppress for unchecked casts (when working with generics) but I also tend to use it for function/property/class names not following the coding conventions (eg. for DSLs) or “unused” to add constant extensions to classes.  
When to use suppress is something that has to be decided on a case by case basis

---

<div class="post-metadata">

**Author:** ![Wasabi375](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.kotlinlang.org/wasabi375/32/4741_2.png) [@Wasabi375](https://discuss.kotlinlang.org/u/Wasabi375)\
**Post date:** [August 28, 2020, 10:28am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/9 "2020-08-28T10:28:46Z")

</div>

> [@fatjoe79](#):
>
> A compiler flag would be nice that turns the warnings into errors.

There is an open issue for this: [https://youtrack.jetbrains.com/issue/KT-18783](https://youtrack.jetbrains.com/issue/KT-18783)

---

<div class="post-metadata">

**Author:** ![kyay10](https://avatars.discourse-cdn.com/v4/letter/k/c57346/32.png) [@kyay10](https://discuss.kotlinlang.org/u/kyay10)\
**Post date:** [August 28, 2020, 10:43am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/10 "2020-08-28T10:43:56Z")

</div>

> [@wdoker](#):
>
> `HideMembers` increase priority of the marked function

The link that I referenced is the `LowPriorityInOverloadResolution` one, which does the exact opposite of `HideMembers`

---

<div class="post-metadata">

**Author:** ![kyay10](https://avatars.discourse-cdn.com/v4/letter/k/c57346/32.png) [@kyay10](https://discuss.kotlinlang.org/u/kyay10)\
**Post date:** [August 28, 2020, 10:45am UTC](https://discuss.kotlinlang.org/t/extensions-and-backward-compatibility/18949/11 "2020-08-28T10:45:53Z")

</div>

`LowPriorityInOverloadResolution` should be the perfect fit for you as a library author. Just do that little hack that allows you to use it (from the post that I linked to), and then annotate your new `foo` member function with `LowPriorityInOverloadResolution`, so then if any user has a `foo` extension function, it will take over it, but for all the other users who don’t have a `foo` function, they’ll be able to use your new member function normally. The existence of that annotation suggests that JetBrains probably ran into that same problem and had to solve it that way, which probably means that it should at least work in most of the normal cases.

Example:

```auto
// V0
class PublicApi {
	// ...
}

// V1
class PublicApi {
	// add new public member with implementation. Assuming of course that you redefined the annotation as explained in the post
    @LowPriorityInOverloadResolution
	fun foo() {
		// ...
	}
}

// User code using V0
fun PublicApi.foo() {
	// ...
}

// User code that uses the extension that was written with V0, but now is getting compiled against V1
fun main() {
    PublicApi().foo() // resolves to the extension function instead of the member one.
}

// A different user that didn't have an extension foo, but has now updated the library
fun main() {
    PublicApi().foo) // resolves to the new member foo because there aren't any other overloads of the foo function
}
```
