JavaRush /Courses /Kotlin SELF /Declaring and calling functions

Declaring and calling functions

Kotlin SELF
Level 8, Lesson 1
Available

1. Declaring functions

When you are just starting to program, the simplest option seems to be writing all the code in main, top to bottom. And that is totally fine at first: your brain is busy with syntax, not architecture.

But pretty quickly two symptoms appear: you copy the same pieces of code (and then fix bugs in five places), and main turns into a long “sheet” that is scary to touch. Functions are a way to give a name to a repeated action or computation so the code reads like a script: “first we asked for input, then we calculated, then we printed”.

Imagine the program is a recipe. main is “what we do step by step”, and functions are “how exactly to chop an onion” and “how to boil pasta”.

Minimal function: fun name() { ... }

First, let’s look at the simplest kind of function: no parameters and no result. In Kotlin, a function is created using the keyword fun, then comes the name and parentheses with parameters (empty for now), then the body in curly braces.

Important: functions (like main) are usually declared at the top level of the file, meaning not “inside main”. We will cover local functions later, but for now we will stick to the classic approach: functions separately, main separately.

fun printWelcome() {
    println("Welcome to the program!") // Welcome to the program!
}

fun main() {
    printWelcome()
    println("We will start working now...")      // We will start working now...
}

Here printWelcome() is just a “command”. We called it, and it executed. Note two things.

First, declaring a function does not run anything by itself. Until you call printWelcome() from main, the program does not even have to remember that such a function exists.

Second, a function name is not a place for imagination. Good names start with an action: printWelcome, readAge, calculateTotal. With them, the code starts to read almost like plain text.

Parameters and arguments

Almost immediately, you want a function that can work with different data. That is what parameters are for.

Parameters are variables the function receives as input. They are declared in parentheses and must have a type. Arguments are the specific values you pass when calling the function.

Let me put these facts into a small table:

Term Where it lives Example How to understand it
Parameter in the function declaration
name: String
“what the function expects as input”
Argument in the function call
"Kotlin"
“what was actually passed”

Now an example. Let’s make a function that prints a greeting for a specific user:

fun greet(name: String) {
    println("Hi, $name!") // Hi, Kotlin!
}

fun main() {
    greet("Kotlin")
    greet("world")
}

Here name: String is a parameter, and "Kotlin" and "world" are arguments. Note: the parameter name is available only inside the function body. The function ends, and the parameter is gone.

Functions with multiple parameters

When there are multiple parameters, they are listed separated by commas. And this is where the first “human” mistakes appear: mixing up the argument order is easier than it seems (especially if the types are the same, for example two Int values).

Let’s make a function that prints a “frame” for a title. It seems like a small thing, but such small things train your brain well: you immediately see the benefit of “giving a name to a repeated piece of code”.

fun printTitle(title: String, width: Int) {
    println("=".repeat(width))
    println(title)
    println("=".repeat(width))
}

fun main() {
    printTitle("Report", 10)
}

If you accidentally called printTitle(10, "Report"), the compiler would not let the program compile because the types do not match (and that is good). But if it were printTitle(width: Int, height: Int) the compiler would accept it, but the logic would break. That is why descriptive parameter names are not just aesthetics, but also protection from mistakes.

2. Return values and signatures

Return type: a function as an “expression”, not just an “action”

So far we made functions that print something. But very often a function must compute and return the result back. Then we say the function “returns a value”.

In Kotlin, the return type is written after the parameter list, separated by a colon: : Int, : String, : Boolean, and so on. A basic Kotlin syntax example shows a function sum(a: Int, b: Int): Int that returns a sum.

fun sum(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    val total = sum(10, 20)
    println(total) // 30
}

Try not to be scared of the word return: in the next lecture we will break down how exactly a function “finishes” and what it means to “return a value”. For now, fix the main idea: if a function has a return type : Int, then the result of calling it can be assigned to a variable and used later.

An important observation: calling a function that returns a value is an expression. That means you can substitute it inside a string or another computation.

fun sum(a: Int, b: Int): Int {
    return a + b
}

fun main() {
    println("2 + 3 = ${sum(2, 3)}") // 2 + 3 = 5
}

We literally inserted the function result into a string template. You already know how to do that with variables; now we are learning to do it with functions.

Function signature as a contract

When you see a function, it is useful to perceive its header as a contract. This helps a lot when the codebase grows and you no longer remember what is written where.

Let’s take an example:

fun average(total: Int, days: Int): Double {
    return total.toDouble() / days
}

Its signature is:

  • name: average
  • parameters: (total: Int, days: Int)
  • return type: Double

And that is almost everything you need to use the function. Even without reading the body, you can understand: “give me total and days, I will return a Double”.

You can picture it as a small diagram:

flowchart LR
    A["Arguments in the call<br/>average(7000, 7)"] --> B["Function parameters<br/>total: Int, days: Int"]
    B --> C["Function body<br/>computations"]
    C --> D["Call result<br/>Double"]

If you can read signatures, you start programming faster: you jump between files less, rely less on intuition, and rely more on the contract.

Parameters inside a function are read-only

Beginners often want to do this: “I received a parameter, now I will adjust it and return it.” But Kotlin does not allow reassigning parameters. This is intentional: parameters are input data, not working variables. Think of them as val variables. That is exactly how the Kotlin compiler treats them.

So you cannot do this (the compiler will complain):

fun makeLouder(text: String): String {
    text = text.uppercase() // Compilation error: Val cannot be reassigned
    return text.uppercase()
}

If you need to change something, create a local variable:

fun normalizeName(name: String): String {
    var result = name.trim()
    result = result.replaceFirstChar { it.uppercase() }
    return result
}

fun main() {
    println(normalizeName("  kotlin  ")) // Kotlin
}

Here name is the input data, and result is the workspace.

3. Mini app “Step tracker”

Now we will build a small but complete example that helps you put everything you learned together. Let it be a console “weekly step tracker”: the program asks for steps for each day, stores them in an array, and prints the total.

Before, you would write everything in main, but now we will start moving repeated steps into functions. We deliberately keep the code simple so the focus is on functions rather than clever tricks.

Helper function for printing a section title

When a program grows, it is nice to have a single output style.

fun printSection(title: String) {
    println()
    println("=== $title ===")
}

fun main() {
    printSection("Step tracker")
}

A function that reads a number

We will do input validation later, but even now we can extract “ask and read” into a separate function.

fun readInt(prompt: String): Int {
    print(prompt)
    val raw = readln().trim()
    val number = raw.toIntOrNull()

    if (number == null) return 0
    return number
}

fun main() {
    val x = readInt("Enter a number: ")
    println("You entered: $x") // e.g.: You entered: 12
}

We will cover how return works a bit later; for now, think of it as “the function must produce an Int, so at the end it gives back a number”.

Function for reading steps for a day

Now we wrap “enter steps for a specific day” into a function with the parameter dayName.

fun readStepsForDay(dayName: String): Int {
    return readInt("Steps for $dayName: ")
}

fun main() {
    val mondaySteps = readStepsForDay("Monday")
    println("Monday: $mondaySteps") // e.g.: Monday: 5400
}

Notice how the code starts to read naturally: readStepsForDay("Monday") is almost a sentence.

Function for summing an array

We have an IntArray. We already know how to iterate it with a loop and accumulate the sum. Now we will give that action a name.

fun sumSteps(steps: IntArray): Int {
    var total = 0
    for (x in steps) {
        total += x
    }
    return total
}

fun main() {
    val data = intArrayOf(3000, 5000, 4500)
    println(sumSteps(data)) // 12500
}

This function is useful not only in the step tracker: it is also an example of how a function packages a long loop and returns the final result.

Putting everything into one main script

Let’s assemble a minimal working “weekly tracker” from small functions. This chunk will be longer than usual—that is normal, because main is intended to be a script that a human reads.

fun printSection(title: String) {
    println()
    println("=== $title ===")
}

fun readInt(prompt: String): Int {
    print(prompt)
    val raw = readln().trim()
    val number = raw.toIntOrNull()
    if (number == null) return 0
    return number
}

fun readStepsForDay(dayName: String): Int {
    return readInt("Steps for $dayName: ")
}

fun sumSteps(steps: IntArray): Int {
    var total = 0
    for (x in steps) total += x
    return total
}

fun main() {
    printSection("Weekly step tracker")

    val days = arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
    val steps = IntArray(7)

    for (i in days.indices) {
        steps[i] = readStepsForDay(days[i])
    }

    val total = sumSteps(steps)
    println("Total steps: $total") // e.g.: Total steps: 42000
}

What is important and interesting here:

  • Each function has a clear name and a small size.
  • Parameters show “what is needed to work”: dayName, prompt, steps.
  • The return type shows “what the function gives back”: Int.
  • main stopped being a dump of details and turned into a script: “show title → collect data → calculate → print”.

4. Typical mistakes when declaring and calling functions

Mistake #1: forgot to specify a parameter type.
Beginners are often tempted to write fun greet(name) { ... }, like in some languages. Kotlin cannot do that: the parameter type is required. The correct version is fun greet(name: String) { ... }. It is annoying at first, and then it saves you from very strange bugs when you “passed the wrong thing to the wrong place”.

Mistake #2: mixing up parameters and arguments (and therefore getting confused in explanations).
This may sound like terminology for the sake of terminology, but in practice it helps. Parameters are what is declared in the function; arguments are what you actually passed in the call. If you call everything “parameters”, at some point you will not be able to explain to yourself what exactly did not match.

Mistake #3: trying to change a parameter inside a function.
After experience with var, you want to “tweak the input value” right in the parameter. Kotlin does not allow reassigning parameters: they behave like val. If you need to change something, create a local var and change that. The code becomes a bit longer, but much clearer.

Mistake #4: a function does too much at once.
When you are just starting, it seems convenient to write fun doAll()—“input, calculation, and printing”. But then the function cannot be reused: you wanted to “only calculate”, but it also prints; you wanted to “only ask”, but it already messes with the array. A good rule at the start: one function equals one clear step of the script or one computation.

Mistake #5: names like f1, calc, doIt, because “it is obvious anyway”.
While the program is 20 lines—yes, “it is obvious”. When it becomes 200—it is not. A function name should answer the question “what does it do?” or “what does it return?”. sumSteps reads without comments, while calc2 is like a riddle from an escape room.

1
Task
Kotlin SELF, level 8, lesson 1
Locked
Console separators
Console separators
1
Task
Kotlin SELF, level 8, lesson 1
Locked
Welcome screen
Welcome screen
1
Task
Kotlin SELF, level 8, lesson 1
Locked
Room plan
Room plan
1
Task
Kotlin SELF, level 8, lesson 1
Locked
Calendar filter
Calendar filter
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION