Kotlin docs states that "functions are first-class". I'm trying to use a function as a default value of a function extension. However the compiler isn't having any of it:
fun <T> identity(x: T): T = x
fun <T, P> Channel<T>.dedupe(by: (T) -> P = ::identity): ReceiveChannel<T>
{
...
}
The error is Function invocation 'identity(...)' expected which kinda indicates Kotlin isn't really understanding what I want to do at all.
Is there a way?
I don't know why you get this error message, but the problem is type mismatch: the default value must make sense for any type parameters (subject to bounds). I.e. you need a (T) -> P, but ::identity can give you (T) -> T or (P) -> P.
Proof: if you change to
fun <T, P> identity(x: T): P = throw Exception()
fun <T, P> List<T>.dedupe(by: (T) -> P = ::identity): Unit {}
it compiles.
Answer (which came out in comments below):
If P is changed to Any?, we should be able to use ::identity because (T) -> T is a subtype of (T) -> Any?. Unfortunately, it doesn't work, but using a lambda instead of a function reference does:
fun <T> identity(x: T): T = x
fun <T> Channel<T>.dedupe(by: (T) -> Any? = { it }): ReceiveChannel<T>
{
...
}
Related
I want to define an Option type, and then pseudo-polymorphically define a getOrNull operation.
sealed interface Option<out T>
data class Some<out T>(val value: T): Option<T>
object None: Option<Nothing>
fun <T> Option<T>.getOrNull(): T? = when (this) {
is Some<T> -> getOrNull()
is None -> getOrNull()
}
fun <T> Some<T>.getOrNull(): T = value
fun None.getOrNull(): Nothing? = null
This all works fine.
But if I tighten the generic type to disallow Option(null) by specifying T: Any
fun <T: Any> Option<T>.getOrNull(): T? = when (this) {
is Some<T> -> getOrNull()
is None -> getOrNull()
}
then I get
Kotlin: Overload resolution ambiguity:
public fun <T : Any> Option<TypeVariable(T)>.getOrNull(): TypeVariable(T)? defined in root package in file Delme.kt
public fun <T> Some<TypeVariable(T)>.getOrNull(): TypeVariable(T) defined in root package in file Delme.kt
for the line is Some<T> -> getOrNull()
How can I tell the compiler which of the two subclass extension functions it should be calling?
The thing is, when you constrain the T of Option<T>.getOrNull but NOT the T of Some<T>.getOrNull you take away the fact that one was more specific than the other (a subtype). Now, they are just "different", hence the resolution ambiguity (the compiler cannot "order" them by specificity anymore).
You can solve it by also constraining Some<T>.getOrNull with Any:
fun <T : Any> Some<T>.getOrNull(): T = value
Example:
sealed interface Foo<out T> {
val value: T
}
data class Bar<out K: List<Int>>(override val value: K): Foo<K>
fun <T> processFoo(foo: Foo<T>) {
when (foo) {
is Bar -> foo.value.forEach(::println)
}
}
Fails with:
Unresolved reference. None of the following candidates is applicable
because of receiver type mismatch:
public inline fun <T> Iterable<TypeVariable(T)>.forEach(action: (TypeVariable(T)) -> Unit): Unit defined in kotlin.collections
public inline fun <K, V> Map<out TypeVariable(K), TypeVariable(V)>.forEach(action: (Map.Entry<TypeVariable(K), TypeVariable(V)>) -> Unit): Unit defined in kotlin.collections
Why this fails? I expect that if foo is of type Bar then we know that T is a subtype of List<Int>. So we should be able to call forEach on it. Am I wrong?
This problem is simply caused by a typo in your code.
If you replace is Bar with is Bar<*>, the compiler is able to infer that T is a List<Int> in that context and the code compiles.
I expect that if foo is of type Bar then we know that T is a subtype of List. So we should be able to call forEach on it.
Yes, that is true. But T could also implement Map<K, V> at the same time as it implements List<Int> (I don't know of such a type, but it theoretically could exist), in which case you would also be able to call this extension function:
inline fun <K, V> Map<out K, V>.forEach(
action: (Entry<K, V>) -> Unit)
See all the different forEaches here.
To specifically call the forEach defined for Iterable, just do a cast:
// could also cast to List<Int> here - that's a completely safe unchecked cast
(foo.value as List<*>).forEach(::println)
An alternative is to use is Bar<*>, but a (very) slight drawback of this is that, as <*> projects the type of foo.value to be List<Int>, you lose the T. You won't be able to use foo.value in places where a T is expected.
A contrived example would be:
fun <T> processFoo(foo: Foo<T>): T {
return when (foo) {
// you can't return foo.value when you are expected to return T
is Bar<*> -> foo.value.also {
it.forEach(::println)
}
}
}
Why it won't compile? It tells there's some error in list.sortBy
fun <T, R : Comparable<R>> Iterable<T>.sortBy(vararg selectors: (T) -> R): List<T> {
return this.sortedWith(compareBy(*selectors))
}
fun main() {
class Person(val name: String, val age: Int)
val list = listOf(Person("Alex", 20))
val sorted = list.sortBy({ it.name }, { it.age })
println(sorted)
}
The error
Type parameter bound for R in
fun <T, R : Comparable<R>> Iterable<T>.sortBy
(
vararg selectors: (T) → R
)
: List<T>
is not satisfied: inferred type Any is not a subtype of Comparable<Any>
When it tries to infer the type R from the first lambda, it's a Comparable<String>. The second lambda returns an Int or Comparable<Int>, which is not a Comparable<String>, so it fails.
You can use star projection for the Comparable type since it doesn't matter if they match.
fun <T> Iterable<T>.sortBy(vararg selectors: (T) -> Comparable<*>): List<T> {
return this.sortedWith(compareBy(*selectors))
}
So I'm trying to reduce this code and avoid the smart cast hint from IDE.
The idea is I have a nullable variable of type T and I want to either map it to R or I just get R from a supplier in case the variable is null.
I've tried different approaches and came up with this one. Still it gives me the smart cast hint.
fun <T, R> T?.func(mapper: (T) -> R, supplier: () -> R): R =
when(this) {
null -> supplier()
else -> mapper(this) // smart cast
}
But I don't like the need for wrapping one of the lambdas in parenthesis. For example.
fun foo(value: String?): Int =
value.func({ it.length + 20}) { 30 }
This may seem odd but the ideia in my context was to pass the variable as not nullable to a function that produced a R or call a function that generated a R.
fun bar(value: T?): R =
when(value) {
null -> func1()
else -> func2(value) // smart cast
}
Note: I've read this but its not the same.
Following should avoid the smart cast hint
fun <T, R> T?.func(mapper: (T) -> R, supplier: () -> R): R {
return this?.let { mapper(it) } ?: supplier()
}
The following does not compile:
fun<T> doSomething(value: T, action: (value: T) -> String = Any::toString){
//do something
}
The error is:
Kotlin: Type mismatch: inferred type is KFunction1<Any, String> but (T) -> String was expected
Making it work is easy:
fun<T> doSomething(value: T, action: (t: T) -> String = {t -> t.toString()}) = action(value)
However, this leaves me wondering: what is the difference between lambdas and KFunctions? Why do we need both?
Also is there a simpler way to provide Any::toString as the default action?
The reason why the code does not compile has nothing to do with the difference between lambdas and KFunctions. It doesn't compile because the parameter needs to be a function of type (T) -> String, and Any::toString is a function of type (Any) -> String.
When you obtain any function (lambda or otherwise) reference with :: you are using reflection. KFunction is Kotlin's way to to wrap around reflected functions.
As to making Any::toString work - there is a way but you may not like it:
fun <T> doSomething(value: T, action: (t: T) -> String = Any::toString as (T) -> String) {
// ...
}
It would have compiled if do like this:
fun <T> doSomething(value: T, action: (value: Any) -> String = Any::toString) {
//do something
}
or
fun <T : Any> doSomething(value: T, action: (t: T) -> String = Any::toString) {
// ...
}