Home > front end >  Correct understanding of T.() lambda
Correct understanding of T.() lambda

Time:05-08

I'm having trouble understanding lambda.

In particular, things like T.() -> R in the run() is more confusing.

public inline fun <T, R> T.run(block: T.() -> R): R = return block()

Questions

fun main() {
    test {
        this.replace("3","B")
    }
}

fun test(block: String.(Int) -> Unit) {
    "TEST".block(7)
}

In this code, in the block parameter, String.() means to define block as an extension function of the String class? So block requires String value when calling?

and,

fun main() {
    "A".test {
        this.replace("3","B")
    }
}


fun String.test(block: String.(Int) -> Unit) {
    block(7)
}

When calling the block in this code, why don't we need the receiver type?

CodePudding user response:

in the block parameter, String.() means to define block as an extension function of the String class? So block requires String value when calling?

Yes. The block is effectively an extension function on the String class: within the block, this is the relevant String instance.

When calling the block in this code, why don't we need the receiver type?

Because there's already an implicit receiver. test() is itself an extension function on String, so within the body of test(), this is a String. And so you can call any of the methods of String without needing to qualify them with this. — including extension methods such as block.


Consider a simpler case with ‘real’ methods instead of extension methods:

class C {
    fun a() {
        // …
    }

    fun b() {
        a() // Implicit receiver
    }
}

a() is a method of class C, and so it needs a instance of C. But b() doesn't need to specify an instance of C when calling a(), because it already has its own receiver.

It could have been written as this.a(), but there's no need, as this is always an implied receiver when you don't specify one. (Some people seem to like an explicit this., but to me it's just pointless visual noise…)

Although extension methods are implemented a little differently ‘under the covers’, the syntax works in exactly the same way. So the call to block() in your second example has an implicit this..

CodePudding user response:

In particular, things like T.() -> R in the run() is more confusing.

public inline fun <T, R> T.run(block: T.() -> R): R = block()

Here T and R are of a generic type, which could be of any data type Int, String or a Class etc. You get the picture.

block: T.() -> R, it's saying that the block has to be an extension function of type T, that could return any type R and this R could be anything, Unit, String, Class etc.

A lambda returns the value of its last expression, so whatever expression is on the last line of your lambda it would return it, that is of type R.

Now when we use that run method on any object, it gives us the same object as the lambda receiver (this) inside the lambda, because our lambda is an extension function of the same type on which we've called this.

var a = 1
val b = a.run {
        this   6
        "incremented"
    }

On using run method on a, our generic T type becomes an Int and our object a is available inside the lambda as this, because, now it's an extension function of Int.

In lambda our last expression is "incremented" which is a String so here our R becomes a type of String. As the run method is returning this R, the value of variable b becomes incremented.


In this code, in the block parameter, String.() means to define block as an extension function of the String class? So block requires String value when calling?

If block is an extension function then you won't need to pass a String. When you call it on any String it would use that. But if block is not an extension function then you would've to pass it.


fun test(block: String.(Int) -> Unit) {
    "TEST".block(7)
}

.

fun String.test(block: String.(Int) -> Unit) {
    block(7)
}

When calling the block in this code, why don't we need the receiver type?

In latter, the test method is an extension function of String, which is available as this in the method body, and all of the functions of the String are also available, that you can use with or without this on the receiver object. As block is also an extension function of String it can be accessed directly.

Meanwhile, in the former, there is no receiver object of type String is available in the method body, which is why you've to explicitly call it on a String.

  • Related