Operator overloading as extension function - kotlin

In my game I have two players and so I defined a typealias of pair that should indicate that I have two things of the same type and .first belongs to player one and .second belongs to player two:
typealias PlayerPair<A> = Pair<A, A>
Also I have defined this enum class
enum class PlayerNumber {
One,
Two
}
Now i would like to add an operator (as extension function) to access the elements like this
myPair[Player.One]
That is my approch but it doesn't work
operator fun PlayerPair.get(i: PlayerNumber) = when (i) {
PlayerNumber.One -> PlayerPair.first
PlayerNumber.Two -> PlayerPair.sexond
}
Adding an else Branch removes one error, but I don't understand why it is necessary.
The other error is 'One type argument expected for typealias PlayerPair = Pair'.
But I can't figure out what this means in this context.

You can create a generic get function, you just have to specify a type parameter, and that you're extending a PlayerPair<T>:
operator fun <T> PlayerPair<T>.get(i: PlayerNumber): T = when (i) {
PlayerNumber.One -> this.first
PlayerNumber.Two -> this.second
}
Inside the operator, you can refer to the instance of PlayerPair<T> as this, which you can also use implicitly:
operator fun <T> PlayerPair<T>.get(i: PlayerNumber): T = when (i) {
PlayerNumber.One -> first
PlayerNumber.Two -> second
}

Related

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.

Kotlin "expected no parameters" when attempting to return inline lambda

I'm trying to write a Kotlin function which returns a lambda taking a parameter. I'm attempting to use code like the following to do this:
fun <T> makeFunc() : (T.() -> Unit) {
return { t: T ->
print("Foo")
}
}
Note: In the actual program, the function is more complex and uses t.
Kotlin rejects this as invalid, giving an 'Expected no parameters' error at t: T.
However, assigning this lambda to a variable first is not rejected and works fine:
fun <T> makeFunc() : (T.() -> Unit) {
val x = { t: T ->
print("Foo")
}
return x
}
These two snippets seem identical, so why is this the case? Are curly braces after a return statement interpreted as something other than a lambda?
Additionally, IntelliJ tells me that the variable's value can be inlined, whereas this appears to cause the error.
There is a curious moment in the design of functional types and lambda expressions in Kotlin.
In fact, the behavior can be described in these two statements:
Named values of functional types are interchangeable between the ordinary functional type like (A, B) -> C and the corresponding type of function with the first parameter turned into receiver A.(B) -> C. These types are assignable from each other.
So, when you declare a variable that is typed as (T) -> Unit, you can pass it or use it where T.() -> Unit is expected, and vice versa.
Lambda expressions, however, cannot be used in such free manner.
When a function with receiver T.() -> Unit is expected, you cannot place a lambda with a parameter of T in that position, the lambda should exactly match the signature, a receiver and the first parameter cannot be converted into each other:
Shape of a function literal argument or a function expression must exactly match the extension-ness of the corresponding parameter. You can't pass an extension function literal or an extension function expression where a function is expected and vice versa. If you really want to do that, change the shape, assign literal to a variable or use the as operator.
(from the document linked above)
This rule makes lambdas easier to read: they always match the expected type. For instance, there's no ambiguity between a lambda with receiver and a lambda with implicit it that is simply unused.
Compare:
fun foo(bar: (A) -> B) = Unit
fun baz(qux: A.() -> B) = Unit
val f: (A) -> B = { TODO() }
val g: A.() -> B = { TODO() }
foo(f) // OK
foo(g) // OK
baz(f) // OK
baz(g) // OK
// But:
foo { a: A -> println(a); TODO() } // OK
foo { println(this#foo); TODO() } // Error
baz { println(this#baz); TODO() } // OK
baz { a: A -> println(a); TODO() } // Error
Basically, it's the IDE diagnostic that is wrong here. Please report it as a bug to the Kotlin issue tracker.
You are defining a function type () -> Unit on receiver T, there really isn't a parameter to that function, see "()". The error makes sense. Since you define the function type with T as its receiver, you can refer to T by this:
fun <T> makeFunc(): (T.() -> Unit) {
return {
print(this)
}
}

Kotlin member and extension at the same time

In an attempt to understand more about Kotlin and play around with it, I'm developing a sample Android app where I can try different things.
However, even after searching on the topic for a while, I haven't been able to find a proper answer for the following issue :
Let's declare a (dummy) extension function on View class :
fun View.isViewVisibility(v: Int): Boolean = visibility == v
Now how can I reference this function from somewhere else to later call invoke() on it?
val f: (Int) -> Boolean = View::isViewVisibility
Currently gives me :
Error:(57, 35) Type mismatch: inferred type is KFunction2 but (Int) -> Boolean was
expectedError:(57, 41) 'isViewVisibility' is a member and an extension
at the same time. References to such elements are not allowed
Is there any workaround?
Thanks !
Extensions are resolved statically, where the first parameter accepts an instance of the receiver type. isViewVisibility actually accept two parameters, View and Int. So, the correct type of it should be (View, Int) -> Boolean, like this:
val f: (View, Int) -> Boolean = View::isViewVisibility
The error message states:
'isViewVisibility' is a member and an extension at the same time. References to such elements are not allowed
It's saying that the method is both an extension function, which is what you're wanting it to be, and a member. You don't show the entire context of your definition, but it probably looks something like this:
// MyClass.kt
class MyClass {
fun String.coolStringExtension() = "Cool $this"
val bar = String::coolStringExtension
}
fun main() {
print(MyClass().bar("foo"))
}
Kotlin Playground
As you can see the coolStringExtension is defined as a member of MyClass. This is what the error is referring to. Kotlin doesn't allow you to refer to extension function that is also a member, hence the error.
You can resolve this by defining the extension function at the top level, rather than as a member. For example:
// MyClass.kt
class MyClass {
val bar = String::coolStringExtension
}
fun String.coolStringExtension() = "Cool $this"
fun main() {
print(MyClass().bar("foo"))
}
Kotlin Playground
A better fit is the extension function type View.(Int) -> Boolean:
val f: View.(Int) -> Boolean = View::isViewVisibility
But actually the extension types are mostly interchangeable (assignment-compatible) with normal function types with the receiver being the first parameter:
View.(Int) -> Boolean ↔ (View, Int) -> Boolean
I faced the same problem when I declared extension function inside another class and try to pass that extension function as parameter.
I found a workaround by passing function with same signature as extension which in turn delegates to actual extension function.
MyUtils.kt:
object MyUtils {
//extension to MyClass, signature: (Int)->Unit
fun MyClass.extend(val:Int) {
}
}
AnyClass.kt:
//importing extension from MyUtils
import MyUtils.extend
// Assume you want to pass your extension function as parameter
fun someMethodWithLambda(func: (Int)->Unit) {}
class AnyClass {
fun someMethod() {
//this line throws error
someMethodWithLambda(MyClass::extend) //member and extension at the same time
//workaround
val myClassInstance = MyClass()
// you pass a proxy lambda which will call your extension function
someMethodWithLambda { someIntegerValue ->
myClassInstance.extend(someIntegerValue)
}
}
}
As a workaround you can create a separate normal function and invoke it from an inline extension method:
inline fun View.isVisibility(v: Int): Boolean = isViewVisibility(this, v)
fun isViewVisibility(v: View, k: Int): Boolean = (v.visibility == k)
You can't call directly the extension method because you don't have the implicit this object available.
Using either a type with two parameters (the first for the implicit receiver, as #Bakawaii has already mentioned) or an extension type should both work without any warnings at all.
Let's take this function as an example:
fun String.foo(f: Int) = true
You can use assign this to a property that has a two parameter function type like this:
val prop: (String, Int) -> Boolean = String::foo
fun bar() {
prop("bar", 123)
}
Or, you can use an extension function type, that you can then call with either of these two syntaxes:
val prop2: String.(Int) -> Boolean = String::foo
fun bar2() {
prop2("bar2", 123)
"bar2".prop2(123)
}
Again, the above should all run without any errors or warnings.