In Kotlin, functions are the first class citizens. A function can be passed as an argument to another function, return it as a value from other functions, and assign it to a variable or data structure. To facilitate this, Kotlin uses specialized constructs to represent a function. In this article we will learn how we can use functions just like any other expressions in Kotlin.
Higher Order function
Higher order functions can take a function as an argument or return a function as a value or do both.
fun add(first: Int, second: Int, sum: (Int, Int) -> Int) {
println(sum(first, second))
}
fun sumOfTwoNumbers(a: Int, b: Int): Int = a + b
fun main(args: Array<String>) {
add(2, 3, ::sumOfTwoNumbers)
}
In this example, the function add is a higher order function because its last parameter sum is a function. The function type of sum specified in the signature of add() is quite unusual isn’t it? Let’s break down what it means.
This means that the function sum has two arguments of type Int and returns a value that is an Int. To pass a function as a parameter to another function, we have used :: before the function name as shown in line 8.
Rules of function Type
We have seen how a function type looks like in fig. 2. Let’s see some more variations of it.
- To represent a function that takes multiple arguments and has a return type, we would declare it as.
- To represent a function type with empty arguments, we would use something like:
- To representing a function with no return type, we would use the following:
Instantiating Function Type
In fig 1, we have used a callable reference to an existing function i.e ::sumOfTwoNumbers to represent a function passed as a value for the parameter sum.
Alternatively, we could also directly pass the code block. We can achieve that in two different forms – lambda expression and anonymous function.
A lambda expression
An example of a Lambda expression is shown below.
Line 8 in fig. 1 can be replaced with
An anonymous function
An example of an anonymous function is shown below.
Line 8 in fig. 1 can be replaced with
Lambda expression and anonymous function are the function literals which means they are a function that is not declared but directly passed as an expression. The above-mentioned lambda and anonymous functions are equivalent to a regular function as shown below.
Lambda Expression syntax
A lambda can be defined as follows
A lambda expression is enclosed inside the curly braces. The parameters declaration and the body are present inside the braces separated by -> symbol.
If the return type of the lambda expression is not Unit then the last expression inside the body is considered as a return value.
Passing trailing Lambdas
If the last parameter of a function is a lambda expression, then it can be written outside the parentheses. The following two examples are equivalent.
If the lambda expression has only one parameter then we do not have to have a parameter list inside the lambda, instead, we can use it to refer to the parameter.
fun printElements(value: Int, p: (Int) -> Unit) {
p(value)
}
fun main(args: Array<String>) {
printElements(2) { println(it) }
}
Anonymous Function
Let’s look at the lambda expression in fig. 3, the data type of the parameters passed is specified however the return type is not explicitly mentioned. Most of the time, the return type can be inferred automatically however sometimes we might need to specify explicitly, in that case, we can use anonymous functions.
Anonymous functions look very similar to the regular functions except that the function name is omitted.
The data type of the parameters can also be omitted. The return type can be inferred when the function body is an expression otherwise should be mentioned explicitly.
There is a difference between Anonymous function and lambdas while working with the return statement. In an anonymous function, the return statement inside the function block would return from the anonymous function, whereas the return statement inside the block of lambda would return from the enclosing function.
Storing functions in a variable or data structure
In Kotlin, the function like anonymous functions or lambda function can be stored to a variable just like any other value.
fun add(first: Int, second: Int, sum: (Int, Int) -> Int) {
println(sum(first, second))
}
val sum: (Int, Int) -> Int = { a, b ->
println("Operand1 = $a Operand2 = $ b")
a + b
}
fun main(args: Array<String>) {
add(2, 3, sum)
}
In this example the lambda is assigned to a variable sum and the variable sum can be used in the same way as any other variables.
Higher order functions make the life of a programmer easier as we can write a small expressive function that is less prone to bugs and is easy to test. Those small functions can then be combined to perform complex operations.
Thank you for taking the time to read the articles. Please let us know if there is any specific topic you would like to read about.
One response
Thanks for the good article, I hope you continue to work as well.