I have a use case for inferring nested generic types in a function's definition.
Consider the following code:
interface 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!