Isaac Udy
06/03/2022, 12:27 PMinterface Input<T> { ... }
interface Output<T> { ... }
fun <In: Input<T>, T> registerForOutput(): Output<T> { ... }
Based on the type of In, I want to be able to use registerForOutput to create an Output instance of the same generic type.
In this situation, I refer to T as the "nested generic type of `In`" in the context of registerForOutput. To re-word the initial sentence: I want registerForOutput to return an Output with the nested generic type of In.
Consider the following code:
class ConcreteInput : Input<String> { ... }
val output = registerForOutput<ConcreteInput, String>()
The code above provides a developer experience that is not ideal. A developer, despite knowing that ConcreteInput uses String as the generic type for its implementation of Input, still needs to explicitly define String as a secondary generic type of the call to registerForOutput.
This is only a minor inconvenience in the case that Input has a single generic type, but in the case that Input has two, three, or even more generic types, this becomes significantly less enjoyable to work with.
I believe that this could be improved by including an "inferred" keyword, which is usable within generic type definitions.
Consider the following code:
fun <In: Input<inferred T>> registerForOutput(): Output<T> { ... }
In the code above, we state that T, the generic type of the generic In (the "nested generic" of In) should be inferred by the compiler, meaning that it would be possible to write the following code, where the type out output should be `Output<String>`:
val output = registerForOutput<ConcreteInput>()
The following code should infer that the T of registerForOutput is Int, and the type of output should be `Output<Int>`:
val output = registerForOutput<Input<Int>>()
The following code should infer that the T of registerForOutput is Nothing, and the type of output should be `Output<Nothing>`:
val output = registerForOutput<Input<*>>()
Ideally, the inferred keyword that I am suggesting should be compatible with reified, so that it's possible to write the following:
inline fun <In: Input<inferred reified T> registerForOutput(): Output<T> {
println(T::class.java.simpleName)
...
}
I am interested to hear feedback on whether or not this makes sense (or is possible) as a language feature!ilya.gorbunov
06/03/2022, 1:36 PM_. So the first call would look like:
val output = registerForOutput<ConcreteInput, _>()
Not ideal yet, but avoids stating the inferred type.Isaac Udy
06/03/2022, 1:57 PMinterface Input<A, B, C>, this is still undesirable, as registerForOutput would become registerForOutput<ConcreteType, _, _, _>
If the compiler is able to understand that _ is a valid argument, could the compiler completely avoid requiring the _ type arguments?dmitriy.novozhilov
06/03/2022, 2:05 PMIn : Input<T> will be the last type parameter of registerForOutput, not first?
How compiler can understand, that ConcreteInput is type argument for last parameter?Isaac Udy
06/03/2022, 2:32 PMinferred keyword,Isaac Udy
06/03/2022, 2:33 PMregisterForOutput definition would only have a single type parameter:
fun <In: Input<inferred A, inferred B, inferred C>> registerForOutput(): Output<A, B, C>Isaac Udy
06/03/2022, 2:37 PMA, B, and C, and if it failed to infer the types (for example, if the type was *) it would result in a type of Nothing.rnett
06/03/2022, 6:17 PMIsaac Udy
06/04/2022, 1:22 AM