Why does @PublishedAPI only work for internal functions? I have a file-private function I want use from an inline function. I need to make it internal in order to work with @PublishedAPI. But I don’t want to expose this function to the rest of the module.
I understand this has to do with java interoperability. This is a complex topic for me since I write 100% kotlin now, and never have to deal with java. In this article someone said that internal functions in kotlin have mangled names to make them harder to use from java.
What is the current suggestion if I want to make a function private to a file or class but still use it in an inline function? Could a future feature addition of the kotlin language possibly address this issue?
I think this is not about Java interop, but more about how it was implemented internally. As the code of the function is inlined into the call site, that means all members this function touches have to be accessible from the call site. Private functions are not available from the call site, so they can’t be invoked from there.
But I think your point is correct and I personally think we should not be restricted only due to how it is currently implemented. It makes perfect sense to invoke a private function from a public function, so this limitation feels pretty “artifical” to me. When writing Kotlin we should not have to think about the resulting bytecode for one specific target. This is the compiler’s job. And this scenario could be easily implemented by the compiler by creating synthetic accessors - very old technique used already in other similar cases. This way we would not even need @PublishedApi.
And this scenario could be easily implemented by the compiler by creating synthetic accessors - very old technique used already in other similar cases. This way we would not even need @PublishedApi .
This wouldn’t work very well because you’d have no indication in the sources that a private declaration is used from a declaration that is inlined into the client code, so you could accidentally rename it and break the clients. Even if synthetic accessors are used, the accessor’s name still depends on the name of the declaration and there’s no way to customize it. In fact, unfortunately the compiler already works in such a way that if it’s tricked (because of some bug) into generating a private declaration reference in a public inline function, it does so via a synthetic accessor, and that leads to uncomfortable binary compatibility problems (e.g. KT-49030).
I agree with the initial idea though, and supporting @PublishedAPI for private functions IMHO would make sense.
Btw,
In this article someone said that internal functions in kotlin have mangled names to make them harder to use from java.
This claim is incorrect. Internal functions in classes have mangled names to reduce the chances of accidental override of unrelated declarations in public classes on JVM. This way, you can have a public base class in one module, a public derived class in another module, and they both can have an internal fun foo() which will not be overridden at runtime because their JVM signatures are different. Interoperability with the Java language is not relevant here. Also note that mangling does not happen for internal top-level functions because inheritance is not possible there.
I’m no expert of binary compatibilty issues, but wouldn’t it possible to just make the private method public during compilation (failing compilation if someone uses the “supposely private” API directly)?
A workaround that I currently use is to use @RequiresOptIn with level error to make the use of the “supposely private” API more alerted for the user, but it’s cumbersome to do it every time.
If we don’t want to use the private keyword, I’d personally be happy with a @PrivatePublishedAPI annotation that has similar effect to @PublishedAPI + @RequiresOptIn annotation, namely it should:
make an function/field accessible only privately
remove auto completion from non-private scope (just like for a private members)
make compilation fail if tried to access it from a non-private scope
It’s common to use inlining in the API (e.g. to solve the function colorization problems), but the fact that you have to expose additional API to do that is quite a bummer.
I found cases where, in order to not expose the additional API internally (in a poorly modularized Project I must say), the dev renounced to inlining and wrote a duplicate API (non-suspend and suspend version) just so that the API would accept the lamba directly…