The difference between lambda and KFunction in Kotlin - kotlin

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) {
// ...
}

Related

Kotlin - Infer type for one of two generic parameters

I am trying to create a function that has two generic types: one reified, and another derived from the context of its usage (since it is an extension function):
inline fun <reified E, A> Either<Throwable, A>.bypassLeft(transformation: Throwable.() -> A): Either<Throwable, A> =
when (this) {
is Either.Left -> when (value) {
is E -> value.transformation().right()
else -> this
}
else -> this
}
The idea would be to call the function just mentioning the reified type, something like:
a.bypassLeft<NoResultException> { "" }
In which "a" is an object of type Either<Throwable,String>
But the compiler is not letting me go away with it, and requires me to specify both generic types, instead of deriving the second one form the object calling the function.
It seemed quite a reasonable thing to be possible, but maybe I am wrong...
Is this possible to achieve? If so, what am I doing wrong?
It's not currently possible with a function to ascribe a single type argument and leave the other inferred. You can achieve what you want if you type the lambda arguments by changing your implementation to not use a receiver type.
I threw in there an additional impl that shows how type args can also be partially applied with a class or other surrounding scope.
import arrow.core.Either
import arrow.core.right
inline fun <reified E : Throwable, A> Either<Throwable, A>.bypassLeft(
transformation: (E) -> A //changed to regular arg not receiver
): Either<Throwable, A> =
when (this) {
is Either.Left -> when (val v = value) { //name locally for smart cast
is E -> transformation(v).right()
else -> this
}
else -> this
}
class Catch<A>(val f: () -> A) { //alternative impl with partial type app
inline fun <reified E : Throwable> recover(
recover: (E) -> A
): Either<Throwable, A> =
Either.catch(f).fold(
{
if (it is E) Either.Right(recover(it))
else Either.Left(it)
},
{
Either.Right(it)
}
)
}
suspend fun main() {
val x: Either<Throwable, Int> = Either.Left(StackOverflowError())
val recovered = x.bypassLeft {
s: StackOverflowError -> //here infers E
0 // here infers A
}
println(recovered) // Either.Right(0)
val notRecovered: Either<Throwable, Int> =
Catch {
throw NumberFormatException()
1
}.recover<StackOverflowError> { 0 }
println(notRecovered) // Either.Left(java.lang.NumberFormatException)
}
This is possible as of Kotlin v1.7.0 with the underscore operator.
The underscore operator _ can be used for type arguments. Use it to automatically infer a type of the argument when other types are explicitly specified:
interface Foo<T>
fun <T, F : Foo<T>> bar() {}
fun baz() {
bar<_, Foo<String>>() // T = String is inferred
}
In your example, it would be possible like this:
a.bypassLeft<NoResultException, _> { "" }

Kotlin arrow.kt - Option.getOrElse is accepting other type than T

I am trying to use the Option.getOrElse() method.
According to the source:
inline fun <R> fold(ifEmpty: () -> R, ifSome: (A) -> R): R = when (this) {
is None -> ifEmpty()
is Some<A> -> ifSome(t)
}
fun <T> Option<T>.getOrElse(default: () -> T): T = fold({ default() }, ::identity)
But when I call getOrElse with a lambda that returns a value of type other than type T, it does not show any error compile time or runtime.
val value1 = Some("val")
// No error
value1.getOrElse { true }
It does not seem right. What am I doing wrong?
This is because Option is covariant (you can see it's declared as Option<out A>), so value1 is also an Option<Any> and { true } is inferred to be () -> Any.

Replace lamda in an extension function

This is an extension function:
fun <T, R> Collection<T>.fold(initial: R, combine: (acc: R, nextElement: T) -> R): R {
var accumulator: R = initial
for (element: T in this) {
accumulator = combine(accumulator, element)
}
return accumulator
}
Is it possible to replace the second parameter which is a function with a separate function. For example, something that would look similar to this:
fun <T, R> Collection<T>.fold(initial: R, someFun)
fun someFun (acc: R, nextElement: T) -> R): R {
var accumulator: R = initial
for (element: T in this) {
accumulator = combine(accumulator, element)
}
return accumulator
}
You can use two colons to pass reference to the function:
var collection = listOf<String>()
collection.fold(3, ::someFun)
fun <T, R> someFun(acc: R, nextElement: T): R {
var accumulator: R = acc
// ...
return accumulator
}
I'm not sure why do you need to extract a function this way. The desired code in question does not compile and to propose a working alternative it's required to know your actual intent.
For example if you don't want to spell a long function signature in the type of a parameter, perhaps because you have a lot of such functions taking that type of function parameter and you afraid of making a mistake in that signature, you can extract the functional type declaration into a type alias:
typealias Combiner<R, T> = (acc: R, nextElement: T) -> R
and then use that type alias in the function declaration:
fun <T, R> Collection<T>.fold(initial: R, combine: Combiner<R, T>): R {
var accumulator: R = initial
for (element: T in this) {
accumulator = combine(accumulator, element)
}
return accumulator
}

Can functions be default parameter values?

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>
{
...
}

Kotlin - overload function with lambda arguments

Suppose I want to have a function that checks whether a property value differs in two given objects. If so, a handler lambda should be executed.
fun <P>onChange(oldObj: P, newObj: P, select: (P) -> Any, handler: (P) -> Unit) {
val newValues = select(newObj)
val oldValues = select(oldObj)
if (oldValues != newValues) { handler(newObj) }
}
It works totally fine for the following use case:
data class TestObj(
val foo: String,
val bar: String? = null
)
val oldObj = TestObj(foo = "foo")
val newObj = TestObj(foo = "foo1", bar = "bar")
onChange(oldObj, newObj, { it.foo }) {
print("foo did change: ${it.foo}")
}
Now I want to be able to check if bar has changed.
If I try to to overload the function like
fun <P>onChange(oldObj: P, newObj: P, select: (P) -> Any?, handler: (P) -> Unit) {
...
}
the compiler throws an Duplicate method name&signature exception.
How do I overload the function or modify the signature in order to support a select lambda that is able to return an optional?
(P) -> Any? is a subtype of (P) -> Any, so if you just have
fun <P>onChange(oldObj: P, newObj: P, select: (P) -> Any?, handler: (P) -> Unit)
you can pass a (P) -> Any there. Of course, with overloading you can have two different implementations, but if behavior is actually different, this seems like a very bad idea :)
Change the name. They have the same JVM signature because nullable types don't exist at the byte code level. They are a Kotlin feature enforced at compile time.