After creating a handful of functions for some task, some of which have generic parameters, I eventually call a ktor function httpClient.get<T>(someUrl). But, in order for this to compile, the calling function must have a reified type param, which implies it must also be inline. Okay, no problem; but then that functions calling function must be inline with reified type param. And its calling function, and so on. To top it off, it also implies previously private functions can no longer be private if called by non-private functions, so my interfaces goes from
fun <T> A()
fun <T> B()
private fun <T> C()
to
inline fun <reified T> A()
inline fun <reified T> B()
inline fun <reified T> C()
Much less pretty, IMO. Is there no way to have syntactic sugar for this (automatic reification perhaps?), such that suddenly needing to call a fun with reified type param doesn’t necessitate changing every function in the hierarchy? It feels very non-user friendly in an otherwise very user friendly language.
Not really. Automatic reification sounds like a nice idea but as soon as you realize the downsides it’s impossible.
The issue is that reification relies on inlining of functions which means no inheritance and no recursion. This is not something that the compiler should automatically force on the programmer. Also inlining can greatly increase the size of the generated bytecode which is another downside.
I never used ktor so I can’t really comment on that part, but I never read anything about long callchains of inlined functions, so maybe it’s a problem with the design of your system and not ktor. But as I said, I never used ktor myself so maybe someone else can help you with that part. I doubt that changing all functions to reified inline functions is a viable solution to your problem though.
Understood; I can imagine auto-reification would work often, but when it breaks would not be fun to debug. ktor isn’t the meat of this topic though; I think the deserialization of HTTP responses relies on annotations on the type, which are lost due to type erasure. I guess the other option is to examine the object at runtime and fill in parameters that correspond to the values in the response json, but that approach is not encouraged due to performance impact.
Conceptually, any fun deserializeSomeJson<T>(json: String) knows that it needs type information of T; if it is able somehow to express that in it’s compiled signature, code using this function could pass in the relevant type/annotation information automatically. Maybe something like that isn’t possible; I’m more trying to express the problem than say I have a good solution
I’ve got no experience with Ktor whatsoever, but you could probably look at how get<>() is implemented and copy that implementation into a regular function that takes a KClass<> parameter (because pretty much everything that you can do with a reified type parameter you can do with a KClass). You can also create a small wrapper over A() that uses a reified type instead
With that, now you can have:
inline fun <reified T> A(){
A(T::class)
}
fun <T> A(c: KClass<T>)
fun <T> B(c: KClass<T>)
private fun <T> C(c: KClass<T>){
...
httpClient.get(someUrl, c)
}
It looks pretty complicated, but it may work for your use case.
In practice, you don’t need to define the function as inline, but you can get the type parameter automagically.