Compile error when pass lambda functions

Hello,
I got a compile error when passing lambda function as parameters. But when I use local val to store it’s reference. Then compile error just disappeared. Can someone help me figure out what happened here?

class Card {
    fun pay(i: Int) { println(i) }
}

class Main {

    val c = Card()

    fun badCall(f: Card.() -> Unit) { // badCall(Lkotlin/jvm/functions/Function1;)V
        c.f()
    }

    fun goodCall(f: (Card) -> Unit) { // goodCall(Lkotlin/jvm/functions/Function1;)V
        f.invoke(c)
    }
}

fun main(args : Array<String>) {
  val f = Card::pay
  val m = Main()

  m.badCall { x: Card -> f.invoke(x, 10)}  // Compile error !! : "Expected no parameters"
  val method  = { x: Card -> f.invoke(x, 10)}  // store it to local val
  m.badCall(method)                        // it compiles now

  m.goodCall { x: Card -> f.invoke(x, 10)} // it's okay for both case
  m.goodCall(method)
}

The error message is : “Error:(23, 17) Kotlin: Expected no parameters”
Byte code of badCall and goodCall looks exactly the same. But badCall won’t compile if I pass lambda function directly to it.
(I do some simple search on forum, but I didn’t find related topics. If the problem had been answered, please tell me. Thansk!)

Your badCall() function takes an extension lambda. Extension lambda’s are compiled to take the receiver as the first parameter.

class Card {
    fun pay(i: Int) { println(i) }
}

class Main {

    val c = Card()

    fun badCall(f: Card.() -> Unit) { // badCall(Lkotlin/jvm/functions/Function1;)V
        c.f()
    }

    fun goodCall(f: (Card) -> Unit) { // goodCall(Lkotlin/jvm/functions/Function1;)V
        f.invoke(c)
    }
}

fun main(args : Array<String>) {
  val f = Card::pay
  val m = Main()

    //sampleStart
  //m.badCall { x: Card -> f.invoke(x, 10)}
  m.badCall { f.invoke(this, 10)}  //sampleEnd
  // m.badCall { x: Card -> f.invoke(x, 10)}
  val method  = { x: Card -> f.invoke(x, 10)}  // store it to local val
  m.badCall(method)                        // it compiles now

  m.goodCall { x: Card -> f.invoke(x, 10)} // it's okay for both case
  m.goodCall(method)
}

As for why it works when passing in the function reference and not when providing a receiver as the first parameter, I suspect it is either,

  1. a bug
  2. the intended behavior since it is an extension lambda and you’re passing in a lambda with a param
  3. a bug that should not let you use m.badCall(method) since you’re passing in a lambda with a param when there is no explicit param

My personal pick is #2, the intended behavior.

Try renaming badCall and goodCall to the same name and notice how the method resolution changes. There would no longer be a compiler error on m.badCall { x: Card -> f.invoke(x, 10)}, but instead there would be a compiler error on m.goodCall(method) for ambiguity.

Thank you very much, it’s very helpful :smiley: