Cannot choose between ()->Unit and T.()->Unit


#1

Im very new in Kotlin and must be miss something, so please show me workaround.

I create a class for store any available listener types (static methods, interfaces, class members and lambdas) in one place. It work but I have one annoying problem: Kotlin cant automatically find right overloaded function for lambda functions.

Here is head part of implementation:

class Event<in LT : Any>(listenerEventMethod : LT.(CallSource) -> Unit) {

  fun call(Source : CallSource) {}

  fun <T : Any> add(eventUser : T, listenerObject : LT) {}
  fun <T : Any, S : CallSource> add(eventUser : T, closureMethod : T.(src : S) -> Unit) {}
  fun <T : Any, S : CallSource> add(eventUser : T, functionMethod : (src : S) -> Unit) {}
  fun <T : Any, S : CallSource> addF(eventUser : T, functionMethod : (src : S) -> Unit) {}
}

Implementation doesnt matter, the problem is in usage of Event.add function.

If I try to use one of overloaded functions “add” Kotlin cannot choose which to use and I forced to create special function “addF” for use with lambdas.
Its very annoying because lambdas are must usable type of listeners.

Is where any problems with my code or its just impossible to use single add for all types?

Note: Its works fine for anonymous functions, problem only with lambdas. Its strange, both has exactly the same type.

Here is test case with problem and Kotlin error diagnostics.

interface CallSource

class Test : CallSource {
  //test interface used by listeners
  interface TCall { 
    fun call(src : CallSource) {}
  }
  //event holder for type: (CallSource)->Unit
  val OnChange = Event(TCall::call)
  //notify all attached listeners
  fun F() { OnChange.call(this) }
}

//class which set listeners
class TestUserVoid(val t : Test) {
  //TEST entry
  fun F() {
    //add listener as anon function
    val v = { it : Test ->
      println("lambdaVAR: $it")
    }
    t.OnChange.add(this, v)

    //add listener as lambda
/* PROBLEM:
Error:(149, 16) Kotlin: Cannot choose among the following candidates without completing type inference:
public final fun <T : Any, S : CallSource> add(eventUser: TestUserVoid, functionMethod: (src: Test) -> Unit): EventBase defined in jm.test.ktest.Event
public final fun <T : Any, S : CallSource> add(eventUser: TestUserVoid, closureMethod: TestUserVoid.(src: Test) -> Unit): EventBase defined in jm.test.ktest.Event

    t.OnChange.add(this) { it : Test ->
      println("lambdaINL: $it")
    }
*/
    //forced to use non-overloaded function to set lambda listener
    t.OnChange.addF(this) { it : Test ->
      println("lambdaF: $it")
    }

    //emulate call listeners
    t.F()
  }
}

fun main(args : Array<String>) {
  val t = Test()
  TestUserVoid(t).F()
}

#2

A lambda with receiver looks exactly the same in source code as a regular lambda. Therefore, when you’re passing a lambda expression to an overloaded method, there is no way for the Kotlin compiler to determine which of the overloads you intend to access.

I think you need to decide whether an event listener is a lambda with receiver or a regular lambda, and to provide only a single overload corresponding to your decision. From the API design perspective, it doesn’t really make sense to support both.


#3

I was hoping Kotlin can differ KFunction and Function somehow.
Why it works if I split lambda declaration and use?..

Anyway, thank you.
Remove one of functions is not an option. Whole idea was to allow use closures (class member) as listener like this:

class TestUser1(val t : Test) {
  fun idClassMethod(src : Test, pos : Int) {
    println("closure callback: $src ($pos)")
  }

  fun F() {
    t.OnPosition.add(this, TestUser1::idClassMethod)
  }
}

Lambdas catch context but closures dont.
Interfaces for java, lambdas for simple cases and closures for big code blocks without context catch.


#4

In Kotlin 1.1, you’re able to use bound callable references for this: add(this, this::idClassMethod)


#5

Thanks for hint, didnt know what.
This syntax does exactly the same as:
add(this) { isClassMethod() }
but creates new class intead of using INSTANCE like lambda does.
Its very small overhead but I didnt see why to use this code syntax fo my task.

After little digging with “Kotlin Bytecode” I found solution for my issue:

class B {
  fun <T : Any> add(eventUser : T, f : Function1<Int, Unit>) {}
  fun <T : Any> add(eventUser : T, f : Function2<T, Int, Unit>) {}

  fun idF(i : Int) {}

  fun FF() {
    add(this) { it : Int ->  }
    add(this, this::idF)
    add(this, B::idF)
  }
}

Now it works for all types as I expect.

But I was surprized what Kotlin generates new class for EVERY usage of this::idF or B::idF.
I hope it will use single instance for each class but no.
This is the answer why my application with heavy DSL usage grows so fast…

Thanks for answers.
The topic may be closed.