Understanding Lambda in Kotlin (Part 2/3)
A Higher Order Function is a function that accepts lambda as a parameter or returns the lambda.
They allow us to pass specific behavior to a generalized function.
As discussed in the previous lambda p1 blog, lambda has 3 parts
- Lambda declaration (the contract)
- lambda invocation (the bridge)
- lambda body ( the Executable part)
Let's see some Real-life Working scenarios of Lambda:
- The
isSystemReqDone
HOF function is called, having the lambda declaration (which says it will accept a boolean value & will return nothing) - Inside
isSystemReqDone
, the lambda is invoked withtrue
(which redirected the flow of control back to lambda body inside { } ) - The lambda body receives the
true
argument and executes its logic, printing "CALLING API"
Highly Reusable Lambda
fun main() {
println("add half of sum: ${giveHalf(5, 10) { x, y -> ((x + y).toDouble()/2) }}")
println("add half of Sub: ${giveHalf(10, 3) { x, y -> ((x - y).toDouble()/2) }}")
println("add half of Multiply: ${giveHalf(5, 10) { x, y -> ((x * y).toDouble()/2) }}")
}
fun giveHalf(x: Int, y: Int, converter: (Int, Int) -> (Double)): Double {
val result = converter.invoke(x, y)
return result
}
Above giveHalf() has multiple parameters and one of them is lambda which is offloading the specific calculation logic to another piece of code, where we can access the same Function differently.
giveSumHalf
becomes highly reusable. It doesn't care how the calculation happens, just that it gets a result. Therefore With Lambdas, we can pass different calculation logic at runtime
let's use Functions instead of Lambda in the Above Scenario
fun main() {
println("fun : ${giveHalf(4,5)}")
}
fun giveHalf(x: Int, y: Int): Double {
// ... some...calc....
val result = giveSumHalf(x, y)
return result
}
Now, working with the Normal function we are fixed with a specific calculation which is been held by the giveSumHalf() function thus being tightly coupled and we need to write separate functions for each variation of sum, subtract, and multiply.
RunTime Beauty of Lambda
fun main() {
var lastName = "Singh"
debug1 { "lambda: Aman $lastName" }
debug2("Aman $lastName")
}
var isWorking = false
fun debug1(function: () -> String) {
if(isWorking) println(function.invoke())
}
fun debug2(value: String) {
if(isWorking) println(value)
}
here both debug1 & debug2 are working the same task of concatenating the string value and printing the value when Boolean isWorking is true.
In debug2 the Normal function approach, the code flows line by line therefore it will concat the Aman $lastName part and pass it to the debug2() function and then will print based on isWorking value.
Here the value will not print because isWorking is set to false, but still, the concat process is being held(which is ok but not efficient in front of lambda)
The Runtime beauty of lambda shows when isWorking is false,it will not even concate the String Aman $lastName
HOW: The flow of control hits the lambda debug1 (Line_no21) it immediately jumps to — fun debug1(Line_no29), where it has now seen the lambda declaration and Now knows exactly how lambda needs to work.
The function. invoke() (Line_no30)redirects the flow of control back to lambda body(Line_no22)
but here it is being blocked by isWorking (false Check), Thus escaping the concat process.
Function returning a lambda:
/*NOTE: the way to invoke lambda returning is > fun(param1)(lambdaExp) */
fun giveHalf2(expression: String): (Int, Int) -> Double {
var result: Double = 0.0
if (expression.equals("add")) {
return { x, y -> (x + y).toDouble() }
} else if (expression.equals("sub")) {
return { x, y -> (x - y).toDouble() }
} else {
return {x,y-> result}
}
}
// function returning lambda
val resultLamdaReturn = giveHalf2("add").invoke(5,4)
println("resultLamdaReturn: $resultLamdaReturn")
Here the giveHalf2 function takes a parameter & returns lambda.
NOTE: the Lambda fundamentals remain the same where invoke() act as a bridge between declaration & lambda body.
lets see the Flow of Code here:
Here the lambda body is inside the giveHalf2() & invocation is being done in fun main(), unlike the case of Function having lambda as parameter.
References:
- https://amitshekhar.me/blog/higher-order-functions-and-lambdas-in-kotlin
- https://www.oreilly.com/library/view/head-first-java/9781492091646/ch12.html
- https://kotlinlang.org/docs/lambdas.html
In the next part3 we will discuss how Lambda is used as a core component in building the scope function, Extenstion function & Jetpack Compose Event driven Structure.
Please feel free to reach out to me on LinkedIn and Twitter.