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?
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.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With