Kotlin - overload function with lambda arguments - kotlin

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.

Related

Compose blocks with different dispatch receivers

Setup:
interface AProvider {
fun getA(): String
}
interface BProvider {
fun getB(): String
}
fun a(block: AProvider.() -> Unit) {}
fun b(block: BProvider.() -> Unit) {}
With this, I can nest the two functions as follows
val x = a { b { getA(); getB() } }
Now I would like to abstract this particular pattern to a higher level function so that I can pass the inner block and call both getA() and getB() in it, e.g. something like this:
val y = { l: ??? -> a { b(l) } }
val z = y { getA(); getB() }
The questions are
Is this possible with the proposed definition of y?
If yes, what should be the type of l?
If not, is there some other definition where I can get access to multiple dispatch receivers inside a lambda?
Note: This is related to my other SO question
You can do this with context receivers, which allows you to specify multiple receivers for one lambda. The type of y would be:
(context(AProvider, BProvider) () -> Unit) -> Unit
That is, a function that takes another function as a parameter, and returns Unit. The function parameter that it takes also returns Unit, but has AProvider and BProvider as its context receivers.
val y: (context(AProvider, BProvider) () -> Unit) -> Unit = { l ->
a { b { l(this#a, this#b) } }
}
val z = y { getA(); getB() }
Notice that when we call l, we pass the context receivers, this#a and this#b, as if they are regular parameters.
This makes z a Unit as well, which is kind of weird. y returns whatever a returns after all, so perhaps you did not intend a to return Unit.

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: How to specify a named arguent with a variable?

Suppose I have two methods:
private fun method1(a: A): A {
return a.copy(v1 = null)
}
private fun method2(a: A): A {
return a.copy(v2 = null)
}
Can I write something like:
private fun commonMethod(a: A, variableToChange: String): A {
return a.copy($variableToChange = null)
}
Another words, can I use a variable to refer to a named argument?
If I understand correctly what you are trying to archive I would recommend to pass a setter to the method e.g.
fun <A> changer (a: A, setter: (a: A) -> Unit ) {
// do stuff
setter(a)
}
Is this what you are looking for?
A possible solution for this problem (with usage of reflection) is:
inline fun <reified T : Any> copyValues(a: T, values: Map<String, Any?>): T {
val function = a::class.functions.first { it.name == "copy" }
val parameters = function.parameters
return function.callBy(
values.map { (parameterName, value) ->
parameters.first { it.name == parameterName } to value
}.toMap() + (parameters.first() to a)
) as T
}
This works with all data classes and all classes that have a custom copy function with the same semantics (as long as the parameter names are not erased while compiling). In the first step the function reference of the copy method is searched (KFunction<*>). This object has two importent properties. The parameters property and the callBy function.
With the callBy function you can execute all function references with a map for the parameters. This map must contain a reference to the receiver object.
The parameters propery contains a collection of KProperty. They are needed as keys for the callBy map. The name can be used to find the right KProperty. If a function as a parameter that is not given in the map it uses the default value if available or throws an exception.
Be aware that this solution requires the full reflection library and therefore only works with Kotlin-JVM. It also ignores typechecking for the parameters and can easily lead to runtime exceptions.
You can use it like:
data class Person (
val name: String,
val age: Int,
val foo: Boolean
)
fun main() {
var p = Person("Bob", 18, false)
println(p)
p = copyValues(p, mapOf(
"name" to "Max",
"age" to 35,
"foo" to true
))
println(p)
}
// Person(name=Name, age=15, foo=false)
// Person(name=Max, age=35, foo=true)

The difference between lambda and KFunction in 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) {
// ...
}

Inferring only some type parameters in Kotlin

I have a method with two type parameters, only one of which can be inferred from arguments, something like (no need to comment this cast is evil, the body is purely for the sake of example)
fun <A, B> foo(x: Any, y: A.() -> B) = (x as A).y()
// at call site
foo<String, Int>("1", { toInt() })
However, the compiler can tell B is Int if A is String. And more generally, if it knows A, B can be inferred.
Is there a way to only provide A at the call site and infer B?
Of course, the standard Scala approach works:
class <A> Foo() {
fun <B> apply(x: Any, y: A.() -> B) = ...
}
// at call site
Foo<String>().apply("1", { toInt() })
I was interested in whether Kotlin has a more direct solution.
Based on this issue/proposal, I'd say no(t yet):
Hello, I am proposing two new feature for kotlin which go hand in
hand: partial type parameter list and default type parameters :) Which
in essence allows to do something as the following:
data class Test<out T>(val value: T)
inline fun <T: Any, reified TSub: T> Test<T>.narrow(): Test<TSub>{
return if(value is TSub) Test(value as TSub) else throw ClassCastException("...")
}
fun foo() {
val i: Any = 1
Test(i).narrow<_, Int>() // the _ means let Kotlin infer the first type parameter
// Today I need to repeat the obvious:
Test(i).narrow<Any, Int>()
}
It would be even nicer, if we can define something like:
inline fun <default T: Any, reified TSub: T> Test<T>.narrow(): Test<TSub>{
return if(value is TSub) Test(value as TSub) else throw ClassCastException("...")
}
And then don't even have to write _
fun foo() {
val i: Any = 1
Test(i).narrow<Int>() //default type parameter, let Kotlin infer the first type parameter
}