Understanding Lambda in Kotlin (Part 2/3)

Droid Aman
4 min readMar 22, 2024

--

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:

Working of Lambda in HigherOrderFunctions
  1. The isSystemReqDone HOF function is called, having the lambda declaration (which says it will accept a boolean value & will return nothing)
  2. Inside isSystemReqDone, the lambda is invoked with true (which redirected the flow of control back to lambda body inside { } )
  3. 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.

Function Working

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:

Function returning Lambda

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:

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.

If you liked this blog, don’t forget to hit the 👏 . Stay tuned for the next one!

--

--