Functions — naming a recipe
A function gives a name to a block of steps so you can run it whenever you like, from anywhere, without writing it out again. Everything you build from Part 3 onwards is functions calling functions.
The same six lines, in four places
Imagine a dice game. When a player wins you want to print a little banner:
1println("*******")
2println("YOU WIN")
3println("*******")Fine — until you need the same banner in four places. Now the same three lines exist four times. Change the stars to hyphens and you have four edits to make, and you will forget one.
There is a better move: give those three lines a name.
1fun banner() {
2 println("*******")
3 println("YOU WIN")
4 println("*******")
5}Now anywhere in the program you write banner() and all three lines run. Four places, one definition. Change it once and every use changes with it.
Think about a recipe card.
Nobody writes "crack two eggs, add milk, whisk, heat the pan…" in the middle of a shopping list. They write "make pancake batter" and keep the details on a card in a drawer.
That is a . The name on the card is the function's name. The steps on the back are its body. Getting the card out and following it is the function.
And notice what the card buys you. You can follow it on Tuesday and again on Friday without rewriting anything. You can hand it to somebody else. And when you improve the recipe, every future pancake improves — because there is only one card.
Making one
1fun greet() {
2 println("Hello!")
3}Four pieces, left to right:
fun— the keyword that says "a function starts here".greet— the name. Same rules as any other name, and by convention it starts with a verb, because a function does something.()— the round brackets. Empty for now; Lesson 1.9 fills them.{ ... }— the body: the steps that run when it is called.
To run it, write its name with brackets:
1fun main() {
2 greet()
3 greet()
4}That prints Hello! twice. The brackets are how Kotlin knows you mean do it now rather than just naming it.
You have been writing a function since Lesson 1.1. main is one — the particular function Kotlin looks for and calls when you press Run. Functions you define outside main go above or below it, at the top level of the file; both work.
Handing something in
A function that always does exactly the same thing is limited. Give it a and the same recipe works on different values:
1fun greet(name: String) {
2 println("Hello, $name!")
3}name: String says: this function needs one piece of information, it is called name inside the body, and it must be a String. Now the must supply one:
1greet("Ada")
2greet("Grace")The value you hand over is an . Two words for two sides of the same handover: the parameter is the slot in the definition, the argument is the value at the call.
Leave the argument out and Kotlin says No value passed for parameter 'name'. Hand over the wrong kind and it says Type mismatch. The on the parameter is a contract, and both sides are held to it.
Handing something back
So far our functions have done things. Often you want one to work something out and give you the answer:
1fun double(n: Int): Int {
2 return n * 2
3}The : Int after the brackets is the — a promise about what comes back. Inside, return is the word that actually hands it over, and it ends the function immediately: nothing after a return runs.
Now double(21) is not an instruction, it is a value. You can use it anywhere a number belongs:
1println(double(21))
2val total = double(5) + double(10)If a function returns nothing, you write no return type at all. Kotlin quietly calls that type ; you will see the word in error messages, and now you know what it means: "nothing useful comes back".
When the whole body is one expression, Kotlin lets you drop the brackets and the return entirely:
1fun double(n: Int) = n * 2
2fun isEven(n: Int) = n % 2 == 0These are called single-expression functions. They are extremely common in real Kotlin, and the return type is inferred — though writing it out is still allowed, and helps on anything non-obvious.
Why bother?
Beyond avoiding repetition, functions do three quieter things that matter more as programs grow:
- They name an idea.
isEven(n)says what it means.n % 2 == 0makes the reader work it out. The code becomes the documentation. - They give you one place to fix things. A bug in a function is one bug. The same code copied five times is five bugs, and you will fix four.
- They keep their mess to themselves. Names declared inside a function vanish when it ends — that is again. A function cannot accidentally trample a name used somewhere else.
Aim for functions that do one thing and are named after that thing. If the name needs the word "and", it is probably two functions.
A complete program, stepped through
- Open Pocket Studio, tap Projects, open KotlinLab, then open
Lab.kt. - Select everything in the file — including
fun main()itself this time — and delete it. - Type in the whole fifteen-line program from the walkthrough above.
greetanddoublego abovemain, at the left-hand edge, not inside it. - Tap Run. The Output panel should show
Hello, Ada!,Hello, Grace!,42,Total: 30. - Add a third call,
greet("you"), and run again. One new line, no new logic. - Now shorten
double. Replace all three of its lines with the single linefun double(n: Int) = n * 2and run. Identical output. - Break it on purpose: delete the
returnword fromdouble(if you put the long version back) and run. Read A 'return' expression required in a function with a block body. - Break it a second way: change
greet("Ada")to justgreet()and run. Read No value passed for parameter 'name'. Put it back.
return to every route out of the function — including inside every branch of an if. Or, if it genuinely returns nothing, delete the : Int promise.greet("Ada"). Lesson 1.9 shows how to give a parameter a default so the caller may leave it out.Unit in that message just means "this function returns nothing"."5" instead of 5..toInt() if the value genuinely arrives as text.- A names a block of steps so you can it from anywhere, as often as you like, without repeating yourself.
fun name() { ... }defines one;name()runs it.mainis the one Kotlin runs for you.- A is the slot in the definition; the is the value you hand over at the call. Kotlin checks the of each.
- A after the brackets promises what comes back, and hands it over — ending the function immediately. No return type means : nothing comes back.
- Single-expression functions drop the brackets:
fun double(n: Int) = n * 2. - Aim for one job per function, and name it after that job.
- Next: parameters get much more comfortable — default values so callers can leave things out, and named arguments so a call reads like a sentence.