Introduction
In this article, we will learn about functions and all the methodologies of functions with some practice questions. We will explore the functions, compact functions, default values, filters, lambdas, and higher-order functions. You can dive into each of the topics independently.
This article is part of the Kotlin Learning Series for programmers. If you read the articles in order, you'll benefit greatly from this series.
Previous Articles
Function in Kotlin
Like in any computer programming language, a function in kotlin is also a block of code that performs a specific task and can be called by other parts of the program. Functions are used to organize code into modular and reusable units, making it easier to write and maintain large programs.
Syntax
return_Type fun function_Name( Arguments (if any) ){
//body of function
//if no return type than returning -> Unit
}
Functions are defined using the fun keyword followed by the function's name. The brackets "()", as in other programming languages, are for function arguments, if any. Curly braces enclose the function's code. If this method doesn't return anything, it has no return type.
main() function in Kotlin
As in other languages, main() is the starting function point of the execution. Let's see creating a Kotlin file & running the main function.
Open IntelliJ IDEA and select any project (or create a new one). There is a list of files and folders on the left side of your IDE project screen. You will find the directory under src > main > kotlin on the project. Create a new Kotlin class by selecting New > Kotlin Class/File from the context menu of the Kotln folder.
Write a main function like the one below.
fun main(args: Array<String>) {
println("Hello, learners!")
}
Passing arguments into main()
You must supply any arguments to the program differently because you are running your program from IntelliJ IDEA rather than from the command line.
Go to Main Menu, then select Run > Edit Configurations. The Run/Debug Configurations window popup. In the Program arguments: type 'function' as a field and click OK.
Write a main function like the one below.
fun main(args: Array<String>) {
println("Hello learner we are learning, ${args[0]}.")
}
Everything has value in Kotlin
In Kotlin, every statement has value, and if it doesn't return anything, then its value is "kotlin.Unit". See the example below :
val temperature = 25
val isHot = if (temperature > 50) true else false
println(isHot) //print false
Note - There exist loops that defy the rule that "everything has a value." For loops such as for and while, there is no sensible value. Hence they have no values. The compiler throws an error if you try to attach a loop's value to something.
Exploring Functions
Default arguments - You can pass parameters in Kotlin by parameter name. The default value is used if a caller does not supply an input. You can also set default values for arguments. You can avoid writing numerous overload versions of the same method later on when you construct methods (member functions).
Let's write a function to watch movies where a movie ticket is compulsory, but taking snacks can be an additional parameter. This will look like below.
fun watchMovie(hasTicket: Boolean, Snacks : Boolean = false){
// here we can print based on values
println( isUserWatchingMovie(hasTicket) +DoUserNeedSnacks(Snacks) )
}
We can call this function from the main in three ways. First, call the default, then the function and pass the snacks parameter then call the function by naming the snacks parameter.
fun main(args: Array<String>) {
watchMovie(true) // deufault value for snacks
watchMovie(true,true) // positional arguments
watchMovie(true, Snacks = true) // named paramater
}
Compact functions - Now, let's print something from the watch movie function from one more function that will return a value based on the hasTicket parameter.
fun isUserWatchingMovie(hasTicket: Boolean): String {
return if (hasTicket) "User can watch movie " else "User cann't watch "
}
We can resize it and make it more readable with the compact function. Let's make one more function with a compact function.
fun DoUserNeedSnacks(needSnacks: Boolean): String = if (needSnacks) "and have snacks " else ""
Function scope - Kotlin functions can be declared at the top level in a file, unlike languages like Java, C#, and Scala (top-level definition is accessible since Scala 3). This eliminates the requirement to build a class to house a function. Kotlin functions can be locally declared as member functions and extension functions in addition to top-level functions.
Local functions - Kotlin supports local functions, which are functions inside other functions.
fun add(a:Int){
fun add(a:Int){
}
add(5) // calling inner add
}
Member functions - A member function is a function that is defined inside a class or object.
class SampleClass {
fun foo() { print("Foo") }
}
Member functions are called with dot notation.
Sample().foo() // creates instance of class Sample and calls foo
Filters – lazy and eager
In Kotlin, a filter is a function that allows you to extract a subset of elements from a collection that meets specific criteria.
As an extension function on collections and sequences, the filter function is defined. The criteria for choosing elements from the collection are specified in a lambda expression that is required. The lambda returns a boolean value after receiving each collection element as input. The element is included in the output collection if the value is true. Else, it is excluded.
Create a filter
Let's define a list of fruits that contain the list of some fruits. You can take a list according to your choice.
val fruits = listOf("avocado", "pumpkin", "mango", "grapes", "papaya", "apple")
Now in the main function, let's extract a subset from the fruits that starts with the letter 'a'. The code for the filter condition is in curly braces {}, and it refers to each item as the filter loops through. If the expression returns true, the item is included.
fun main(args: Array<String>) {
println(fruits.filter { it[0] == 'a' })
}
The output of the following program will be like the below.
[avocado, apple]
Compare eager and lazy filters
You're familiar with filters in other languages, you may wonder whether filters in Kotlin are eager or lazy. Is the result list created immediately or when the list is accessed? In Kotlin, it happens whichever way you need it to. By default, the filter is eager, and each time you use the filter, a list is created.
To make the filter lazy, you can use a Sequence, which is a collection that can only look at one item at a time, starting at the beginning and going to the end. Conveniently, this is exactly the API that a lazy filter needs.
val eager = fruits.filter { it[0] == 'p' }
println("eager: $eager")
Below that code, evaluate the filter using a Sequence with asSequence(). Assign the sequence to a variable called filtered, and print it.
// lazy, will wait until asked to evaluate
val filtered = fruits.asSequence().filter { it[0] == 'p' }
println("filtered: $filtered")
Here you return the filter results as a Sequence, the filtered variable won't hold a new list—it'll hold a Sequence of the list elements and knowledge of the filter to apply to those elements. Whenever you access elements of the Sequence, the filter is applied, and the result is returned to you.
Force evaluation of the sequence by converting it to a List with toList(). Print the result.
val newList = filtered.toList()
println("new list: $newList")
Run your program and observe the output:
new list: [pumpkin, papaya]
Lambdas and high-order functions
lambdas
Kotlin supports lambdas which is an expression that makes a function. Kotlin functions are first-class functions, meaning they can be stored in some variable, and data structures can also be passed as arguments and returned from other functions. Instead of declaring a named function, we can declare a function with no name, which can now be passed as data. In other languages, lambdas are called anonymous functions, function literals, or similar names.
Like regular functions, lambdas can have parameters All the parameters of Lambdas go on the left part of the arrow -> and the executable code goes to the right part of the arrow. Once any variable has been assigned by Lambdas, can call it just like a function. Lambdas function is generally started by "{" and ended by "}".
Code
var num = 4
val squareOperation = { number : Int -> number * number }
println(squareOperation(num))
The SquareOperation variable has a Lambdas function that can perform square operations.
Let's see how we can declare Lambdas with syntax. Lambdas type is calculated by the type inference, but we can explicitly typecast the lambdas. See the below example.
val squareOperation : (Int) -> Int = { number : Int -> number * number }
Here we have declared a variable SquareOperation, and defined types like this will take Int as an argument and return an Int, and assigned the lambda like previously.
High-order functions
A higher-order function is a function that takes functions as parameters or returns a function. Previously we create a higher-order function called a filter. You passed the following lambda expression to filter as the condition to check:
{it[0] == 'a'}
Lambda's true value lies in their ability to be used to build higher-order functions, where one function is an argument to another. Let's understand higher-order functions with the basic example below.
fun updateNumber(number: Int, operation: (Int) -> Int): Int {
return operation(number)
}
The Function takes two arguments, the first is an integer, and the second is a function that takes an integer and returns an integer. The function that was provided as the second argument is called in the code body along with the first argument.
To use this function, we will pass an integer and a function like the one below.
val squareOperation : (Int) -> Int = { number : Int -> number * number }
println(updateNumber(8, squareOperation) )
=> 64
It's not necessary for the function you supply to be a lambda, it can just be a standard named function. Use the :: operator to specify the argument as a standard function. By doing this, Kotlin will understand that you are supplying a regular function reference rather than attempting to invoke the lambda function. Let's understand this by the example below.
fun main(){
fun updateNumber(number: Int, operation: (Int) -> Int): Int {
return operation(number)
}
fun doSquare(number: Int) = number*number
println(updateNumber(8, ::doSquare) )
}
Last parameter call syntax- Any function parameter that accepts a function should ideally be the last parameter when using Kotlin. Kotlin features a specific syntax for working with higher-order functions called the last parameter call syntax, which enables you to write even shorter code. In this situation, you can give a lambda as a function parameter without having to enclose it in brackets. see below example :
var number = 5
number = updateNumber(number){
num -> num*num
}
println(number)
=>25
Summary
In today's article, we saw how to implement functions in kotlin and saw some of the important use of functions with their types. Functions really make life easier, try solving problems using function is a good task it will distribute your code into small blocks of code and make it easier to debug and identify problems. Hope this has been a helpful guide for you, i will be helpful for me if you write your genuine comment about this article. Thank you!
Take a quiz on this article here,
Question 1. In the function "fun hasUserWatchedMovie(hasTicket Boolean, userName String = "", needSnacks Boolean = false)" which parameter is required parameter?
Question 2. Can we write a function without a class in Kotlin?
Question 3. Write a higher-order function that will take a number and a lambda. The lambda will take a double value and return the number in round-off and absolute formate.
Happy Coding!