Kotlin higher order function parameters: Passing subtypes - kotlin

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.

Related

Kotlin: Generic high-order function - Type mismatch

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).

Providing only one type parameter to an extension function with multiple type parameters in Kotlin

Introduction
In Kotlin I have a generic conversion extension function that simplifies conversion of this object of type C to an object of another type T (declared as the receiver) with additional conversion action that treats receiver as this and also provides access to original object:
inline fun <C, T, R> C.convertTo(receiver: T, action: T.(C) -> R) = receiver.apply {
action(this#convertTo)
}
It is used like this:
val source: Source = Source()
val result = source.convertTo(Result()) {
resultValue = it.sourceValue
// and so on...
}
I noticed I often use this function on receivers that are created by parameterless constructors and thought it would be nice to simplify it even more by creating additional version of convertTo() that automates construction of the receiver based on its type, like this:
inline fun <reified T, C, R> C.convertTo(action: T.(C) -> R) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
Unfortunately, I cannot call it like this:
source.convertTo<Result>() {}
because Kotlin expects three type parameters provided.
Question
Given above context, is it possible in Kotlin to create a generic function with multiple type parameters that accepts providing just one type parameter while other types are determined from the call-site?
Additional examples (by #broot)
Imagine there is no filterIsInstance() in stdlib and we would like to implement it (or we are the developer of stdlib). Assume we have access to #Exact as this is important for our example. It would be probably the best to declare it as:
inline fun <T, reified V : T> Iterable<#Exact T>.filterTyped(): List<V>
Now, it would be most convenient to use it like this:
val dogs = animals.filterTyped<Dog>() // compile error
Unfortunately, we have to use one of workarounds:
val dogs = animals.filterTyped<Animal, Dog>()
val dogs: List<Dog> = animals.filterTyped()
The last one isn't that bad.
Now, we would like to create a function that looks for items of a specific type and maps them:
inline fun <T, reified V : T, R> Iterable<T>.filterTypedAndMap(transform: (V) -> R): List<R>
Again, it would be nice to use it just like this:
animals.filterTypedAndMap<Dog> { it.barkingVolume } // compile error
Instead, we have this:
animals.filterTypedAndMap<Animal, Dog, Int> { it.barkingVolume }
animals.filterTypedAndMap { dog: Dog -> dog.barkingVolume }
This is still not that bad, but the example is intentionally relatively simple to make it easy to understand. In reality the function would be more complicated, would have more typed params, lambda would receive more arguments, etc. and then it would become hard to use. After receiving the error about type inference, the user would have to read the definition of the function thoroughly to understand, what is missing and where to provide explicit types.
As a side note: isn't it strange that Kotlin disallows code like this: cat is Dog, but allows this: cats.filterIsInstance<Dog>()? Our own filterTyped() would not allow this. So maybe (but just maybe), filterIsInstance() was designed like this exactly because of the problem described in this question (it uses * instead of additional T).
Another example, utilizing already existing reduce() function. We have function like this:
operator fun Animal.plus(other: Animal): Animal
(Don't ask, it doesn't make sense)
Now, reducing a list of dogs seems pretty straightforward:
dogs.reduce { acc, item -> acc + item } // compile error
Unfortunately, this is not possible, because compiler does not know how to properly infer S to Animal. We can't easily provide S only and even providing the return type does not help here:
val animal: Animal = dogs.reduce { acc, item -> acc + item } // compile error
We need to use some awkward workarounds:
dogs.reduce<Animal, Dog> { acc, item -> acc + item }
(dogs as List<Animal>).reduce { acc, item -> acc + item }
dogs.reduce { acc: Animal, item: Animal -> acc + item }
The type parameter R is not necessary:
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
inline fun <reified T, C> C.convertTo(action: T.(C) -> Unit) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
If you use Unit, even if the function passed in has a non-Unit return type, the compiler still allows you to pass that function.
And there are other ways to help the compiler infer the type parameters, not only by directly specifying them in <>. You can also annotate the variable's result type:
val result: Result = source.convertTo { ... }
You can also change the name of convertTo to something like convert to make it more readable.
Another option is:
inline fun <T: Any, C> C.convertTo(resultType: KClass<T>, action: T.(C) -> Unit) = with(resultType.constructors.first().call()) {
convertTo(this, action)
}
val result = source.convertTo(Result::class) { ... }
However, this will conflict with the first overload. So you have to resolve it somehow. You can rename the first overload, but I can't think of any good names off the top of my head. I would suggest that you specify the parameter name like this
source.convertTo(resultType = Result::class) { ... }
Side note: I'm not sure if the parameterless constructor is always the first in the constructors list. I suggest that you actually find the parameterless constructor.
This answer does not solve the stated problem but incorporates input from #Sweeper to provide a workaround at least simplifying result object instantiation.
First of all, the main stated problem can be somewhat mitigated if we explicitly state variable's result type (i.e. val result: Result = source.convertTo {}) but it's not enough to solve the problem in cases described by #broot.
Secondly, using KClass<T> as result parameter type provides ability to use KClass<T>.createInstance() making sure we find a parameterless constructor (if there's any – if there is none, then result-instantiating convertTo() is not eligible for use). We can also benefit from Kotlin's default parameter values to make result parameter type omittable from calls, we just need to take into account that action might be provided as lambda (last parameter of call) or function reference – this will require two versions of result-instantiating convertTo().
So, taking all the above into account, I've come up with this implementation(s) of convertTo():
// version A: basic, expects explicitly provided instance of `receiver`
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
// version B: can instantiate result of type `T`, supports calls where `action` is a last lambda
inline fun <C, reified T : Any> C.convertTo(resultType: KClass<T> = T::class, action: T.(C) -> Unit) = with(resultType.createInstance()) {
(this#convertTo).convertTo(this#with, action)
}
// version C: can instantiate result of type `T`, supports calls where `action` is passed by reference
inline fun <C, reified T : Any> C.convertTo(action: T.(C) -> Unit, resultType: KClass<T> = T::class) = with(resultType.createInstance()) {
(this#convertTo).convertTo(T::class, action)
}
All three versions work together depending on a specific use case. Below is a set of examples explaining what version is used in what case.
class Source { var sourceId = "" }
class Result { var resultId = "" }
val source = Source()
fun convertX(result: Result, source: Source) {
result.resultId = source.sourceId
}
fun convertY(result: Result, source: Source) = true
fun Source.toResultX(): Result = convertTo { resultId = it.sourceId }
fun Source.toResultY(): Result = convertTo(::convertX)
val result0 = source.convertTo(Result()) { resultId = it.sourceId } // uses version A of convertTo()
val result1: Result = source.convertTo { resultId = it.sourceId } // uses version B of convertTo()
val result2: Result = source.convertTo(::convertX) // uses version C of convertTo()
val result3: Result = source.convertTo(::convertY) // uses version C of convertTo()
val result4: Result = source.toResultX() // uses version B of convertTo()
val result5: Result = source.toResultY() // uses version C of convertTo()
P.S.: As #Sweeper notices, convertTo might not be a good name for the result-instantiating versions (as it's not as readable as with basic version) but that's a secondary problem.

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")
}
}

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.

Type name as value?

Can somebody point me to the documentation explaining what the following means? Especially, I'd like to know why String and Int can be used as shown.
val a: Unit = { _: Any -> String }(Int)
Initially, I had written this:
val a: Unit = { x: Any -> x.toString() }(Unit)
In both cases, I've failed to find the right documentation.
Let's break down the first line.
{ _: Any -> String }
This is a lambda. It takes one parameter, that's marked as never used by giving it the name _. The parameter of this type is specified as Any. The lambda returns String, which refers to the (completely empty) companion object inside the String class. The type of this lambda is therefore (Any) -> String.Companion.
{ _: Any -> String }(Int)
This lambda is invoked by the parentheses after it, passing in Int, which again refers to the companion object inside that class (this one isn't empty, it contains constants). This works, because of course, Int.Companion is a subtype of Any, the expected parameter type.
val a: Unit = { _: Any -> String }(Int)
Finally, the declaration of a with its explicit Unit type forces the lambda to change its inferred type from (Any) -> String.Companion to (Any) -> Unit, so it can return the expected explicit type after it's invoked.
The second line is more of the same, only simpler without the companion objects.
Type name as value?
String and Int are the companion objects (sort of the equivalent to Java's static) of the String and Int classes. The name of a class is synonymous with the name of its companion object, i.e. Int more or less is the same as Int.Companion, etc.
{ _:Any -> String } and { x:Any -> x.toString() } are both lambdas of type (Any) -> String.Companion and (Any) -> String, but since the result of the function call is assigned to a, which is of type Unit, these functions are both inferred to return Unit.
Basically, after type inference:
{ _:Any -> String } is a (Any) -> Unit
{ x:Any -> x.toString() } is also a (Any) -> Unit
This type corresponds to the void type in Java.
Documentation: Unit
So these functions both effectively do nothing.
The first function (of type (Any) -> Unit takes one argument (which is unused) and returns String.Companion.
In this case, it is called with Int.Companion... but the argument is unused, so it doesn't matter.
You could have written:
val lambda: (Any) -> Unit = { _: Any -> String }
val a: Unit = lambda(Int)
The second function calls toString on its parameter, which is in this case Unit. The string representation of Unit is defined as "kotlin.Unit", but since it is assigned to a the function's return type is inferred to be Unit, so the function doesn't return anything useful.
These functions are both quite useless as they return Unit (i.e. void, nothing). What do you want to accomplish?
You defined a lambda { _: Any -> String } of type (Any)-> String.Companion and called it with the Int.Companion object. It always returns the String.Companion object. Does not make much sense. The following might be more readable:
val func = { _: Any -> String.Companion }
val res = func(Int.Companion)
Note that Int and Int.Companion are used interchangeably here (also applies for the String one).
Information about companion objects can be found in the documentation.