Two different ways to call lambda block with receiver?

I’m curious how it is possible to call lambda function with receiver just like belows in two different ways. And what is considered the best practice between two?

Here is a simplified implementation of kotlin ‘with’ construct.
Thank you.

method #1
public inline fun <T, R> with(receiver: T, block: T.() → R): R {
return receiver.block()
}

method #2
public inline fun <T, R> with(receiver: T, block: T.() → R): R {
return block(receiver) // how this is possible?
}

This is because the Kotlin compiler exposes the fact that lambdas with receivers are, under the hood, just a static function with the receiver being the first parameter. This is exposed for convenience-sake and has no crazy implications or downsides.

Thank you Kyay10.

By the way, how can I get the static function generated by the compiler?
I tried Intellij providing decompile tools, but it does not seems to generate what I want to see.

1 Like

Well, this is an inline function, and so instead of generating a real lambda, the compiler just inlines the body of this function with the body of the “lambda” into the call site right away. This is an optimization to make life much easier, but if you want, you can make the function not inline, and then you’ll see the generated lambda

Yes, I know that this is inline function so it is copied to the code.
But the part you mentioned ‘under the hood’ is what i want to see. Actual code translated to like below maybe…?

// this is imaginary code I think compiler generated. But where I can see it?
val function = (block as Function1)
function.invoke(receiver)

Thank you Kyay10

1 Like

Well, that doesn’t get generated at all because the actual block itself also gets inlined. If you want to see this at the call site directly, make the block parameter noinline

1 Like

I managed to generate the code using Intellij. Thank you very much.

// custom function
public inline fun <T, R> with2(receiver: T, block: T.() → R): R {
return block(receiver)
}

// decompiled code
public final Object with2(Object receiver, @NotNull Function1 block) {
int $i$f$with2 = 0;
Intrinsics.checkNotNullParameter(block, “block”);
return block.invoke(receiver);
}

1 Like