Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Kotlin Lambda with receiver accepts random function

Hi I am having a kotlin function

fun extension(name: String, init: LambdaReceiver.() -> Unit) {
    val lambdaReceiver = LambdaReceiver(name)
    lambdaReceiver.init()
}

where the second parameter accepts a lambda with receiver init: LambdaReceiver.() -> Unit, and the structure of LambdaReceiver looks like

class LambdaReceiver(private val name: String) {
    private var test: Int? = null
    fun setTest(int: Int) {
        test = int
        println("$name $test")
    }
}

To consume the above function extension, I am having the below code:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

fun main() {
    extension("testFunc") {
        setTest(1)
    }
}

So far good, but when I modify the consumer like below

fun main() {
    fun someOtherRandomFunction() {
        println("someOtherRandomFunction")
    }
    extension("testFunction") {
        someOtherRandomFunction()
        setTest(1)
    }
}

It was a surprise for me because there is no compilation error. I was expecting the complier to show some error because someOtherRandomFunction does not belongs to LambdaReceiver, whereas the extension’s function second parameter is declared to accepts some function from LambdaReceiver.

Can someone shed light here, how I can restrict the extension function from receiving only function related to LambdaReceiver

>Solution :

See the overload resolution section of the spec for how function calls are resolved.

For a function call that is a simple name (i.e. without an explicit receiver) like setTest(1) or someOtherRandomFunction() here, the following are considered (in order):

  • Local non-extension callables named f in the current scope and its upwards-linked scopes, ordered by the size of the scope (smallest
    first), excluding the package scope;

  • The overload candidate sets for each pair of implicit receivers e and d available in the current scope, calculated as if e is the
    explicit receiver, in order of the receiver priority;

  • Top-level non-extension functions named f, in the order of:

    • Callables explicitly imported into the current file;
    • Callables declared in the same package;
    • Callables star-imported into the current file;
    • Implicitly imported callables (either from the Kotlin standard library or platform-specific ones).

someOtherRandomFunction() is the name of a local non-extension callable, so the call is resolved to that.

LambdaReceiver.() -> Unit doesn’t restrict the functions you can use to those declared in LambdaReceiver. It is basically (LambdaReceiver) -> Unit – a function that takes a LambdaReceiver parameter and returns Unit – except that the "parameter" is called this, the receiver of the function. This allows callers of extension to call methods declared on LambdaReceiver, without an explicit receiver.

// if "extension" takes a (LambdaReceiver) -> Unit instead...
extension("testFunc") { foo ->
    foo.setTest(1)
    // you need to add "foo." as the receiver
}

If LambdaReceiver.() -> Unit prevented all calls to functions that are not declared in LambdaReceiver, it would be very inconvenient. You couldn’t even use things like listOf!

someClassThatHasAList.apply { // apply takes a T.() -> Unit
    // would you really want this to not compile?
    someList = listOf(1,2,3)
}

Also note that because of this order above, you can even hide LambdaReceiver.setTest like this:

fun main() {
    fun setTest(int: Int) {}

    extension("testFunc") {
        setTest(1) // this calls the setTest declared above!
        // to call LambdaReceiver.setTest, you'd need an explicit receiver
        this.setTest(1)
    }
}

because local non-extension callables are looked up first.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading