ProgrammingKotlin Developer

How are higher-order functions and lambda expressions implemented in Kotlin? Describe the nuances of passing and returning functions, syntax features, main limitations, and provide code examples.

Pass interviews with Hintsage AI assistant

Answer.

Higher-order functions are functions that take other functions as parameters or return them. Kotlin uses lambda expressions to conveniently pass behavior as a value.

Example of declaration:

fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int { return operation(a, b) } val sum = operateOnNumbers(3, 2) { x, y -> x + y } // sum = 5

Passing functions:

  • Functions can be passed not only as lambda expressions, but also by reference: :
fun multiply(x: Int, y: Int) = x * y operateOnNumbers(2, 3, ::multiply)

Returning a function:

fun makeMultiplier(factor: Int): (Int) -> Int = { x -> x * factor } val triple = makeMultiplier(3) val result = triple(10) // 30

Features:

  • A lambda can have no more than one unnamed parameter (it).
  • Named parameters can be passed to a function, but types must be explicitly stated if the type cannot be inferred.
  • Lambda expressions are objects (anonymous classes), which affects performance with a large number of calls in hot loops (solved with inline functions).
  • Closures are used for lambdas with variable capturing.

Trick question.

What is the difference between declaring a function type (Int, Int) -> Int and using the type Function2<Int, Int, Int>?

Answer: The syntax (Int, Int) -> Int is just a more "beautiful" declaration (syntactic sugar) for the Function2<Int, Int, Int> interface. In practice, both options are completely interchangeable.

val f1: (Int, Int) -> Int = { x, y -> x + y } val f2: Function2<Int, Int, Int> = { x, y -> x + y }

However, the first option is usually preferred for readability.

Examples of real errors due to lack of knowledge on the nuances of the topic.


Story

In a large event processing system, dozens of lambda expressions were being created inside a loop without using inline functions. This resulted in high GC load and performance degradation, as a separate anonymous function object was created for each call.


Story

When attempting to return a function from another function, the signature of the returned function was not correctly specified, leading to a compilation error and a long search for the cause. The error was due to the absence of parentheses in the type: fun foo(): Int -> Int instead of the correct fun foo(): (Int) -> Int.


Story

The developer tried to use a lambda without explicitly specifying the type as a parameter to another function with an undeclared type, which led to the error "cannot infer a type for this parameter". The problem was solved by explicitly stating the type of the lambda or the function parameter.