Kotlin: Generic high-order function - Type mismatch - kotlin

I'm trying to grasp generics in Kotlin.
In the following sample, I'm trying to constrain the type T and use it inside a high-order function, or just functions in general.
interface A {
fun foo()
}
class bar<T : A> (val g: A, val h: T, val callable: (T) -> Unit ) {
fun test() {
// Polymorphism works as expected
g.foo()
h.foo()
// Type mismatch: inferred type is A but T was expected
callable(g)
// Fine
callable(h)
// Type mismatch: inferred type is A but T was expected
baz(g)
// Fine
baz(h)
}
fun baz(l: T) {}
}
Could you please explain why it doesn't compile?

You declared that T must be a supertype of A.
Let's use a more graphic example.
Assume A is a Person and T is a Teacher. You've declared that a Teacher is a Person - which makes sense. However, the opposite is not true. Not all Persons (A) are Teachers (T).
When invoking both bar and callable you expect a Teacher to be passed in.
You cannot simply call these functions with a Person or A, because that person might not be a Teacher (or T).

Related

Kotlin generics: Cannot sort by type with star projected parameter

I'm trying to sort a list of objects - lets call them StockRows, by their values, which all implement
interface DetailedStockCell <in T: DetailedStockCell<T>> : Comparable<T>
for example, this class represents a value in StockRow:
data class SharesCell(val shares: Int?) : DetailedStockCell<SharesCell> {
override fun compareTo(other: SharesCell): Int {
return this.shares.compareTo(other.shares)
}
}
Now, this is StockRow - with all it's values. It also contains a Map to associate each value to an index.
data class StockRow(
val symbolCell: SymbolCell,
val sharesCell: SharesCell,
val priceCell: PriceCell,
val totalGainCell: TotalGainCell,
val percentOfPortfolioCell: PercentOfPortfolioCell
) {
val columnIndex : Map<Int, DetailedStockCell<*>> = mapOf(
0 to symbolCell,
1 to sharesCell,
2 to priceCell,
3 to totalGainCell,
4 to percentOfPortfolioCell
)
But when I try to sort a List of StockRows by a selected values via columnIndex map, sortBy{} fails to infer the sorted type
val masterList: List<StockRow> = //whatever list
fun sortStocksBy(selectedColumn: Int) : List<StockRow>{
return masterList.sortedBy { stockRow -> stockRow.columnIndex[selectedColumn] }
}
Error:
Type parameter bound for R in inline fun <T, R : Comparable<R>> Iterable<T>.sortedBy(crossinline selector: (T) -> R?): List<T>
is not satisfied: inferred type DetailedStockCell<*> is not a subtype of Comparable<DetailedStockCell<*>>
It's the star projection argument that needs to be recursively fulfilled.
Now, I can get around this by not using Generics at all, and just using casting in each implementation of DetailedStockCell, But I'd like to get this working somehow with Generics.
From what I understand so far, is that there is a clash between in and out type bounds.
Comparable requires an in bound for it's type, but the map holding the values must have an out Any bound to be read successfully by sortBy{}. I mean - this almost works, except that now T in Comparable<T> is shouting it need to be in:
interface DetailedStockCell <out T> : Comparable<T>
data class DetailedStockRow(...){
val columnIndex: Map<out Int, DetailedStockCell<Any>> = mapOf(...)
}
I feel like I'm missing something simple here, so any wise help is appreciated!
You can't really combine sorting and generics like this. Sorting requires knowing the type to sort by so the method signature can be known at runtime, but this isn't possible with generics at runtime because of type erasure.
The existence of the DetailedStockCell doesn't really accomplish anything. sortedBy wants something that's comparable to its own type, but something being a DetailedStockCell doesn't guarantee that to the compiler. It could be comparable to some other class that implements it, but not to instances of the same class. I would just remove that interface and have each cell type implement Comparable<itsOwnType>.
So you need to specifically sort each type with its concrete type. You can drop the columnIndex map and make a function like this:
fun Iterable<StockRow>.sortedByColumn(columnIndex: Int): List<StockRow> {
return when (columnIndex) {
0 -> sortedBy(StockRow::symbolCell)
1 -> sortedBy(StockRow::sharesCell)
2 -> sortedBy(StockRow::priceCell)
3 -> sortedBy(StockRow::totalGainCell)
4 -> sortedBy(StockRow::percentOfPortfolioCell)
else -> error("Nonexistant column: $columnIndex")
}
}

Kotlin higher order function parameters: Passing subtypes

I have run into a problem with function parameters in Kotlin. I will explain the issue with the help of some code.
I created a class hierarchy. When I pass a subtype into a function expecting a parent type, there is no issue.
open class A (val i: Int)
class B (val j: Int) : A(j)
fun f(x: A){
print(x)
}
fun test_f(){
f(A(1))
f(B(1)) //no problem
}
I tried to mimic this with function parameters.
fun g(x: (A)->Int){
print(x)
}
fun test_g(){
val l1 = { a: A -> a.hashCode()}
g(l1)
val l2 = { b: B -> b.hashCode()}
g(l2) //Error: Type mismatch. Required: (A)->Int, Found: (B)->Int
}
It seems that function type (B) -> Int is not a subtype of (A) -> Int.
What is the best way to address this?
My original problem is to define a higher order function in A.h that takes a function z: (A) -> X as parameter. And I want call h on an object of type B and pass a function z: (B) -> X.
Update:
I tried generics with upper bound, yet my issue is not solved. Please find code below:
// Using generics doesn't allow me to pass A.
open class A (val i: Int) {
fun <M: A> g(x: (M)->Int){
print(x(this)) // Error: Type mismatch. Expected: M, Found: A
}
}
You can solve it using generics and an extension function on a generic receiver. Deriving the extension function from your updated sample:
fun <T : A> T.g(x: (T)->Int){
print(x(this))
}
This way it is ensured that the receiver and the first parameter type of the given function are the same, which is either an A or a subtype of it.
What you're trying to do is a conversion from function type (B) -> Int (source) to (A) -> Int (target). This is not a safe conversion.
Your source function (B) -> Int takes any instance which is a B, but not necessarily an instance of type A. More concretely, it cannot handle all arguments that are of type A but not of type B.
Imagine your classes look like this:
open class A
class B : A {
fun onlyB() = 29
}
You can define a function
val fb: (B) -> Int = { it.onlyB() }
val fa: (A) -> Int = fb // ERROR
The function fb will not be able to operate on class A, since A does not have the onlyB() function. As a consequence, you're not allowed to convert it to a function type which takes A parameters.
This concept is called contravariance, meaning that input parameters can only be constrained by becoming more concrete, not more abstract. So, the opposite direction works:
val fa: (A) -> Int = { it.hashCode() }
val fb: (B) -> Int = fa // OK, every B is also an A
In contrast, for return values, the concept of covariance applies. This means that return values are allowed to become more abstract, but not more concrete:
val fInt: (B) -> Int = { it.onlyB() }
val fNum: (B) -> Number = fInt // OK, every Int is also a Number
These relations can be exploited in generic classes, using Kotlin's in (contravariance) and out (covariance) keywords -- see here for detailed explanation.

Type inference for higher order functions with generic return types

The following example is perfectly legal in Kotlin 1.3.21:
fun <T> foo(bar: T): T = bar
val t: Int = foo(1) // No need to declare foo<Int>(1) explicitly
But why doesn't type inference work for higher order functions?
fun <T> foo() = fun(bar: T): T = bar
val t: Int = foo()(1) // Compile error: Type inference failed...
When using higher order functions, Kotlin forces the call site to be:
val t = foo<Int>()(1)
Even if the return type of foo is specified explicitly, type inference still fails:
fun <T> foo(): (T) -> T = fun(bar: T): T = bar
val t: Int = foo()(1) // Compile error: Type inference failed...
However, when the generic type parameter is shared with the outer function, it works!
fun <T> foo(baz: T) = fun (bar: T): T = bar
val t: Int = foo(1)(1) // Horray! But I want to write foo()(1) instead...
How do I write the function foo so that foo()(1) will compile, where bar is a generic type?
I am not an expert on how type inference works, but the basic rule is: At the point of use the compiler must know all types in the expression being used.
So from my understanding is that:
foo() <- using type information here
foo()(1) <- providing the information here
Looks like type inference doesn't work 'backward'
val foo = foo<Int>()//create function
val bar = foo(1)//call function
To put it in simple (possibly over-simplified) terms, when you call a dynamically generated function, such as the return value of a higher-order function, it's not actually a function call, it's just syntactic sugar for the invoke function.
At the syntax level, Kotlin treats objects with return types like () -> A and (A, B) -> C like they are normal functions - it allows you to call them by just attaching arguments in parenthesis. This is why you can do foo<Int>()(1) - foo<Int>() returns an object of type (Int) -> (Int), which is then called with 1 as an argument.
However, under the hood, these "function objects" aren't really functions, they are just plain objects with an invoke operator method. So for example, function objects that take 1 argument and return a value are really just instances of the special interface Function1 which looks something like this
interface Function1<A, R> {
operator fun invoke(a: A): R
}
Any class with operator fun invoke can be called like a function i.e. instead of foo.invoke(bar, baz) you can just call foo(bar, baz). Kotlin has several built-in classes like this named Function, Function1, Function2, Function<number of args> etc. used to represent function objects. So when you call foo<Int>()(1), what you are actually calling is foo<Int>().invoke(1). You can confirm this by decompiling the bytecode.
So what does this have to do with type inference? Well when you call foo()(1), you are actually calling foo().invoke(1) with a little syntactic sugar, which makes it a bit easier to see why inference fails. The right hand side of the dot operator cannot be used to infer types for the left hand side, because the left hand side has to be evaluated first. So the type for foo has to be explicitly stated as foo<Int>.
Just played around with it a bit and sharing some thoughts, basically answering the last question "How do I write the function foo so that foo()(1) will compile, where bar is a generic type?":
A simple workaround but then you give up your higher order function (or you need to wrap it) is to have an intermediary object in place, e.g.:
object FooOp {
operator fun <T> invoke(t : T) = t
}
with a foo-method similar as to follows:
fun foo() = FooOp
Of course that's not really the same, as you basically work around the first generic function. It's basically nearly the same as just having 1 function that returns the type we want and therefore it's also able to infer the type again.
An alternative to your problem could be the following. Just add another function that actually specifies the type:
fun <T> foo() = fun(bar: T): T = bar
#JvmName("fooInt")
fun foo() = fun(bar : Int) = bar
The following two will then succeed:
val t: Int = foo()(1)
val t2: String = foo<String>()("...")
but... (besides potentially needing lots of overloads) it isn't possible to define another function similar to the following:
#JvmName("fooString")
fun foo() = fun(bar : String) = bar
If you define that function it will give you an error similar as to follows:
Conflicting overloads: #JvmName public final fun foo(): (Int) -> Int defined in XXX, #JvmName public final fun foo(): (String) -> String defined in XXX
But maybe you are able to construct something with that?
Otherwise I do not have an answer to why it is infered and why it is not.

Kotlin: Returning Array<E> from function with return type Array<I> if E is enum class that implements interface I

Recently I ran into a problem where I had a function which had to return an Array of Is, in form of all values of enum E, with E implementing interface I, with every code that came to my mind compiler complained about type mismatch:
Error:(x, x) Kotlin: Type mismatch: inferred type is Array<E> but Array<I> was expected
A minimal example:
interface I {}
enum class E: I {
A, B, C;
}
fun getMoreInterfaces(): Array<I> {
return E.values()
}
This happens when trying to assign E.values() to variable of type Array<I>
I am positive that this should be possible since E implements I.
Another thing that I came up while testing is that it works just fine when used like this:
interface I {}
enum class E: I {
A, B, C;
}
fun getMoreInterfaces(): Array<I> {
return arrayOf(E.A, E.B, E.C)
}
I did a lot of searching on this topic but with no luck (perhaps I chose the wrong way to describe it?)
In Kotlin, unlike Java, Array<T> is invariant on T, so, for E that is a subtype of I, Array<E> and Array<I> are not subtypes of each other. See: Variance.
Given that the Array<T> types also store the item type and cannot be subject to fully unchecked casts, your best way to solve this is to create a separate array.
You can do that by either creating an array manually and filling it with the items, like in your example (or by using the constructor Array(n) { ... }), or use .toTypedArray() applied to the list representation of the array (.asList()):
fun getMoreInterfaces(): Array<I> {
return E.values().asList().toTypedArray()
}
(runnable sample)
But basically, you can just go with a List<I> if you are not in performance-critical code, which is more idiomatic for Kotlin than working with arrays, and also simpler.
See also: Difference between List and Array types in Kotlin
Array is an invariant generic type in Kotlin, so if you need to return an instance of Array<I>, you can't return Array<E> instead, even if E is a subtype of I.
But in case if you are only consuming values from the returned array, you can declare its type as Array<out I>. This type is a covariant projection of type Array<I> and it allows you to return both Array<I> and Array<E>.
interface I {}
enum class E: I {
A, B, C
}
fun getMoreInterfaces(): Array<out I> {
return E.values()
}

What is the purpose of having bound class reference return a covariant type?

I'm playing with reflection and I came out with this problem. When using bound class reference via the ::class syntax, I get a covariant KClass type:
fun <T> foo(entry: T) {
with(entry::class) {
this // is instance of KClass<out T>
}
}
As I could learn from the docs, this will return the exact type of the object, in case it is instance of a subtype of T, hence the variance modifier.
However this prevents retrieving properties declared in the T class and getting their value (which is what I'm trying to do)
fun <T> foo(entry: T) {
with(entry::class) {
for (prop in memberProperties) {
val v = prop.get(entry) //compile error: I can't consume T
}
}
}
I found that a solution is using javaClass.kotlin extension function on the object reference, to get instead the invariant type:
fun <T> foo(entry: T) {
with(entry.javaClass.kotlin) {
this // is instance of KClass<T>
}
}
This way, I get both the exact type at runtime and the possibility to consume the type.
Interestingly, if I use a supertype instead of a generic, with the latter method I still get access to the correct type, without the need of variance:
class Derived: Base()
fun foo(entry: Base) {
with(entry.javaClass.kotlin) {
println(this == Derived::class)
}
}
fun main(args: Array<String>) {
val derived = Derived()
foo(derived) // prints 'true'
}
If I got it correct, ::class is equal to calling the java getClass, which returns a variant type with a wildcard, while javaClass is a getClass with a cast to the specific type.
Still, I don't get why would I ever need a covariant KClass, when it limits me to only produce the type, given that there are other ways to access the exact class at runtime and use it freely, and I wonder if the more immediate ::class should return an invariant type by design.
The reason for covariance in bound ::class references is, the actual runtime type of an object the expression is evaluated to might differ from the declared or inferred type of the expression.
Example:
open class Base
class Derived : Base()
fun someBase(): Base = Derived()
val kClass = someBase()::class
The expression someBase() is typed as Base, but at runtime it's a Derived object that it gets evaluated to.
Typing someBase()::class as invariant KClass<Base> is simply incorrect, in fact, the actuall result of evaluating this expression is KClass<Derived>.
To solve this possible inconsistency (that would lead to broken type-safety), all bound class references are covariant: someBase()::class is KClass<out Base>, meaning that at runtime someBase() might be a subtype of Base, and therefore this might be a class token of a subtype of Base.
This is, of course, not the case with unbound class references: when you take Base::class, you know for sure that it's the class token of Base and not of some of its subtypes, so it's invariant KClass<Base>.