Is there any proposal to make it possible to someh...
# language-proposals
j
Is there any proposal to make it possible to somehow define APIs for top-level functions? We are looking at having a strict API/implementation separation in our project and we're wondering what's the best way to achieve this for top-level composables. I'm not convinced by the current obvious solution that is moving the composable declaration inside an interface, but I don't think there is any other way to achieve that separation right now 😕
s
Would expect/actual work in this scenario?
j
Unfortunately no, but yeah that's exactly the kind of declaration/definition API we'd need
o
Looks similar to "KT-79983 # API - Implementation separation(Dependancy Invervesion)", which itself also looks like SPI (
java.util.ServiceLoader
), right?
I mean, if you have SPI, you could have an interface and an implementation, and generate top-level declarations (potentially auto-generated) that just redirect to this interface/implementation.
j
Sorry for the late response, but yes the bug you linked is a great example of what we would need 🙂 This api/impl split really helps in terms of build time and enforces clear API boundaries between modules, it's a bit sad that we don't have a good solution for top-level composables
o
Nice! Thanks! Now comes one more question: if we have the
ServiceLoader
API, you can already do this. I mean, as I stated above: create an interface, annotate it with some annotation (like
@TopLevelApi
), and use KSP to generate top-level declarations. e.g:
Copy code
@TopLevelAPI interface Composables {
  @Composable fun Text(text: String)
}

// KSP will generate something like:
private val composables: Composables = ServiceLoader.load(Composables::class.java).single()
@Composable fun Text(text: String): Unit = composables.Text(text)

// in some other module, you will have implementation:
object ComposablesImpl: Composables {...}
In this case, it will be super clear that the interface defines the API, and the implementation is somehow loaded via
ServiceLoader
. If needed, you can also hide
Composables
from the public API in different ways. This will also allow multiple
Composables
implementations (for tests, per flavor, or other use cases), if needed. Also, if this is an app, you might not need
ServiceLoader
at all and can use any other DI (Metro/Dagger/koin/etc) to link the implementation. Will something like this work for you? Do you see a big downside in such an approach? I mean, changing language is always nice, but it looks like it's already possible with the current Kotlin.