Pocket Studio Academy
HomePart 11.9

Parameters, defaults and named arguments

Full course9 min read·4 questions

Give a parameter a default and callers can leave it out. Name your arguments at the call site and the code says what it means. Together these two features replace whole families of near-identical functions.

What does true mean?

Here is a real line from somebody's app:

kotlin
showDialog("Delete note?", true, false, 2)

Everything about it works. Nothing about it is readable. What is true? What is false? Is 2 a count, a style, a timeout?

To find out you have to go and open the function definition — which means the call site is lying to you about how simple it is. This lesson is about two small Kotlin features that fix exactly this, and they will change how you write every function from here on.

kotlin
1showDialog(
2  message = "Delete note?",
3  cancelable = true,
4  destructive = false,
5  buttons = 2
6)

Same call. Nothing to look up.

Think of it like this

Picture ordering in a café.

You say "a latte" and you get one. You did not say the size, or the milk, or how many shots — the café has a normal answer for each of those, and uses it. That is a default.

But when you do care, you do not recite a list in a fixed order and hope the barista counts correctly. You say "a latte, oat milk, extra shot". Each thing is labelled, so the order does not matter and nothing gets mixed up. That is a named argument.

Kotlin gives you both, on any function you write.

Several parameters

A function can take as many inputs as it needs, separated by commas. Each one gets a name and a :

kotlin
1fun area(width: Int, height: Int): Int {
2  return width * height
3}
4
5fun main() {
6  println(area(3, 4))
7}

By default, are matched by position: the first value goes into the first . That is fine here, because both are Int and multiplication does not care which way round they go.

Now consider this:

kotlin
fun move(from: Int, to: Int) { }

Swap those two at a call and Kotlin cannot help you. Both are Int, both are valid, and your piece moves backwards. This is the bug that named arguments exist to prevent.

Note

Parameters are . You can read width inside the function, but you cannot assign to it — that gives Val cannot be reassigned. If you need a changed version, declare your own var inside the function and copy the value into it.

Default values

Put = something after a parameter and it becomes optional:

kotlin
1fun order(drink: String, shots: Int = 1) {
2  println("$drink with $shots shot(s)")
3}

Now both of these work:

kotlin
1order("Latte")        // Latte with 1 shot(s)
2order("Latte", 2)     // Latte with 2 shot(s)

That is a . The value is used whenever the caller does not supply one.

This matters more than it looks. Without defaults, the usual answer is to write the same function three times with different numbers of parameters — three bodies to keep in step, three places for a bug to hide. One function with defaults replaces the lot.

A default can be anything that produces a value, including a calculation, and it is worked out fresh on each call that needs it.

Tip

Put the parameters callers always supply first, and the optional ones last. It keeps the common call short and the awkward cases still possible.

Named arguments

At the call, you may write the parameter's name before the value:

kotlin
order(drink = "Mocha", shots = 3)

That is a . It buys you two things.

Readability. order("Mocha", 3) needs the definition to decode. order(drink = "Mocha", shots = 3) does not.

Skipping. Once you are naming things, you can leave out any parameter that has a default, even one in the middle:

kotlin
1fun order(
2  drink: String,
3  shots: Int = 1,
4  oat: Boolean = false
5) {
6  println("$drink, $shots shot(s), oat=$oat")
7}
8
9order("Latte", oat = true)

That gets a one-shot latte with oat milk. Without the name, there would be no way to say "skip the middle one".

Named arguments also free you from the order:

kotlin
order(shots = 3, drink = "Mocha")

There is one rule about mixing the two styles: positional arguments must come first. Start naming and you should keep naming. Go back to positional after a name and Kotlin refuses with Mixing named and positional arguments is not allowed.

Careful

Once a function is used by other code, its parameter names are part of its promise, just like its types. Rename drink to beverage and every call that said drink = ... stops compiling. Choose parameter names as carefully as function names.

When to name

You do not need names everywhere. area(3, 4) is perfectly clear. A good habit:

  • Always name a Boolean argument. true on its own never means anything at a call site.
  • Always name when two neighbouring parameters share a type, like move(from = 1, to = 5).
  • Name anything you are skipping past, because you have no choice.
  • Otherwise, if the value reads obviously, leave it positional.

One function, four ways to call it

Try it in Pocket Studio
  1. Open Pocket Studio, tap Projects, open KotlinLab, then open Lab.kt.
  2. Select everything in the file and delete it.
  3. Type in the whole program from the walkthrough above.
  4. Tap Run. The Output panel should show four lines: Latte, 1 shot(s), oat=false, Latte, 2 shot(s), oat=false, Latte, 1 shot(s), oat=true, Mocha, 3 shot(s), oat=false.
  5. Add a fifth call: order("Flat white", 2, true). Run it. All three values supplied positionally — correct, but notice how much less it tells you than the named version.
  6. Now write the same call with names: order("Flat white", shots = 2, oat = true). Same output, and you can read it without scrolling up.
  7. Break it: change the last call to order(shots = 3, "Mocha"). Run and read Mixing named and positional arguments is not allowed — the positional value has nowhere to go once naming has started.
  8. Break it once more: change order("Latte") to order(). Read No value passed for parameter 'drink'. A parameter without a default is never optional.
Error Doctor5 common errors
e: No value passed for parameter 'drink'
MeansA parameter with no default was left out of the call.
FixSupply it, or give it a default in the definition: drink: String = "Latte".
e: Cannot find a parameter with this name: shot
MeansA named argument uses a name the function does not have — nearly always a typo or a stale name after a rename.
FixCheck the spelling against the definition. The parameter here is shots, not shot.
e: Mixing named and positional arguments is not allowed
MeansA positional value appears after a named one. Once you start naming, Kotlin has no reliable way to place the unnamed ones that follow.
FixName the rest of the arguments too, or move all the positional ones to the front.
e: Val cannot be reassigned
MeansYou assigned to a parameter inside the function body. Parameters are val — they hold the value handed in and cannot be changed.
FixDeclare your own var inside the function and copy the value in: var n = shots. Then change n freely.
e: Type mismatch: inferred type is Boolean but Int was expected
MeansThe arguments are in the wrong order, so true has landed where a number was expected.
FixName the arguments — order(drink = "Latte", oat = true) — and this whole class of mistake stops being possible.
Recap
  • A can take several , matched by position at the call.
  • A shots: Int = 1 — makes a parameter optional and removes the need for several near-identical functions.
  • A order(drink = "Mocha") — makes a call readable, lets you skip optional parameters in the middle, and frees you from the order.
  • Positional arguments must come before named ones.
  • Always name Booleans, and always name when two neighbouring parameters share a type.
  • Parameters are : read them, but do not assign to them.
  • Next: one value is rarely enough. Lists hold many things under one name — and reintroduce that off-by-one trap with real consequences.