I'm prototyping some highly declarative code, and the type inference and safety that comes with Kotlin helps a lot. One of the goals is making extensions (subclasses) of the primary types stupidly easy to implement. In order to maintain rich type inference and expressiveness, I've found some success in defining generic extension functions projected against subclasses. All the type information of subclass methods with none of the extra subclass implementation, it's great.
So I'm trying to write a rich generic function that maintains as much type information as possible. The issue creeps up with the fact that this function operates on potentially recursively generic types, and I want to shuffle the generic type parameters.
This would be impossible to describe without an example. So consider:
open class G<in T>
class A<in T> : G<T>()
class B<in T> : G<T>()
class C<in T> : G<T>()
val ba = B<A<*>>()
val cb = C<B<*>>()
We want a function that effectively can do this, except generically
fun B<A<*>>.doTransitiveThing(c: C<B<*>>) : C<A<*>>
{
// implement
}
val ca = ba.doTransitiveThing(cb) // Returns C<A<*>>
Goal Criteria:
C
as a param and returns C
, except with different generic type parameterG
G
so the argument must be C<B<*>>
instead of C<G<*>>
when invoked on B<A<*>>
That describes the gist of the problem. I'm not sure the language is capable of supporting what I want. I'm not sure if type erasure is a factor that makes this impossible, but so far I can't find it so (maybe I could use help if it is so).
The following is close
fun <
TargetGenericType,
Arg1Type: G<*>,
ReceiverType: G<TargetGenericType>,
Arg2Type: G<Arg1Type>,
ResultType: G<TargetGenericType>
>
ReceiverType.doTransitiveThingGeneric(x: Arg2Type): ResultType
{
//implement
}
val ca2 = ba.doTransitiveThingGeneric(cb)
but there are a few problems
G<A<*>>
instead of C<A<*>>
. It would be nice if it could return C
and not lose type information (otherwise I don't really have a use for this function anyway)ReceiverType
is Arg1Type
Thinking ahead, if something like the following was valid Kotlin, I think it would address my problem
fun <
TargetGenericType,
ReceiverBaseType<T>: G<T>,
typealias ReceiverType = ReceiverBaseType<TargetGenericType>,
ParamBaseType<U>: G<U>,
typealias ParamType = ParamBaseType<ReceiverBaseType<*>>,
ResultType: ParamBaseType<TargetGenericType>
>
ReceiverType.doTransitiveThingHigherOrderGeneric(x: ParamType): ResultType
{
//implement
}
Is there a reason it can't be done? e.g. added as a feature on the language? I am sympathetic to logistical reasons against, but I'm curious if it is even possible in principle too.
Last notes:
<T>
and <U>
in the syntax.Higher-Order Function – In Kotlin, a function which can accept a function as parameter or can return a function is called Higher-Order function. Instead of Integer, String or Array as a parameter to function, we will pass anonymous function or lambdas.
The type parameter lets you specify exactly that—instead of “This variable holds a list,” you can say something like “This variable holds a list of strings.” Kotlin's syntax for saying “a list of strings” looks the same as in Java: List<String> . You can also declare multiple type parameters for a class.
We can make generic variable using this kind of syntax: "val destinationActivity: Class<*>". Main part is "*".
There are no direct ways to do this in Kotlin. In order to check the generic type, we need to create an instance of the generic class<T> and then we can compare the same with our class.
Ultimately what I was looking for is higher kinds. I was trying to force it all into one single overly nested type constructor. The manipulations I wanted to accomplish cannot be achieved that way, and must be done using multiple type parameters. The functional library Arrow's description of higher kinds helped me realize that.
In a Higher Kind with the shape
Kind<F, A>
, ifA
is the type of the content thenF
has to be the type of the container.A malformed Higher Kind would use the whole type constructor to define the container, duplicating the type of the content
Kind<Option<A>, A>
. This incorrect representation has large a number of issues when working with partially applied types and nested types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With