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