Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin extension lambdas vs regular lambdas

According to the following source code it seems that regular lambdas are interchangeable with extension lambdas.

fun main(args: Array<String>) {

    val numbers = listOf(1, 2, 3)

    filter(numbers, predicate)
    filter(numbers, otherPredicate)

    println("PREDICATE: ${predicate} " +
        "\nOTHERPREDICATE: ${otherPredicate} " +
        "\nEQUALITY: ${predicate==otherPredicate}")
}

val predicate : Int.() -> Boolean = {this % 2 != 0}
val otherPredicate : (Int) -> Boolean = {it % 2 != 0}


fun filter(list: List<Int>, predicate:(Int) -> Boolean) {
    for(number in list){
        if(predicate(number)){
            println(number)
        }
    }
}

The output (I care about), is the following:

PREDICATE: kotlin.Int.() -> kotlin.Boolean 
OTHERPREDICATE: (kotlin.Int) -> kotlin.Boolean 
EQUALITY: false

The question is why are these lambdas interchangeable? Shouldn't be something different? Is the compiler doing something "smart" under the hood?

like image 831
LiTTle Avatar asked Feb 01 '18 08:02

LiTTle


People also ask

What is the benefit of lambda expressions in Kotlin?

Lambdas are one of the most powerful tools in Kotlin, and in any other modern language, since it allows modelling functions in a much simpler way. The only way we can do this in Java 6 is by declaring interfaces with a single method, and creating anonymous objects that implement those interfaces.

What are the lambdas in Kotlin?

Lambda expression is a simplified representation of a function. It can be passed as a parameter, stored in a variable or even returned as a value. Note: If you are new to Android app development or just getting started, you should get a head start from Kotlin for Android: An Introduction.

What is an extension function in Kotlin?

Kotlin extension function provides a facility to "add" methods to class without inheriting a class or using any type of design pattern. The created extension functions are used as a regular function inside that class. The extension function is declared with a prefix receiver type with method name.

What is trailing lambda in Kotlin?

Passing trailing lambdas According to Kotlin convention, if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses: val product = items.


1 Answers

Differences

It's not entirely interchangeable since "extension lambdas", technically called lambdas with receiver, can be invoked on a receiver, which is not possible for regular lambdas:

predicateWithReceiver(2) //OK
2.predicateWithReceiver() //OK

regularPredicate(2) //OK
2.regularPredicate //Not OK

Lambdas with receiver may be invoked as ordinary functions with an argument but can also be invoked on their receiver object directly (similar to extensions). The more important part is how such lambdas look like on the caller site, i.e. you do not need to use qualifiers to access visible members of that receiver inside such a lambda.

Compilation

This is enabled by compiler techniques. The following demonstrates how 2.regularPredicate looks like on bytecode level (shown as decompiled Java):

  Function1 predicateWithReceiver = ...;
  predicateWithReceiver.invoke(2);

It looks like a regular function call, the translation is taken care of by the compiler.

EDIT

As for a higher-order function such as filter it doesn't really make a difference. See how it is compiled (again depicted as Java):

public static final void filter(@NotNull List list, @NotNull Function1 predicate) {

  //...
     if ((Boolean)predicate.invoke(number)) {
        System.out.println(number);
     }
  }

}

The filter function takes an instance of Function1. Both, regular and lambdas with reiceiver are compiled to exactly such an object. As a result, it doesn't make a difference how you define the argument predicate in your Kotlin code.

like image 64
s1m0nw1 Avatar answered Oct 08 '22 17:10

s1m0nw1