# Named & Default Arguments

You can provide argument names during a function call.

Named arguments improve code readability. This is especially true for long and complex argument lists—named arguments can be clear enough that the reader can understand a function call without looking at the documentation.

In this example, all parameters are Int. Named arguments clarify their meaning:

// NamedAndDefaultArgs/NamedArguments.kt
package color1
import atomictest.eq

fun color(red: Int, green: Int, blue: Int) =
  "($red, $green, $blue)"

fun main() {
  color(1, 2, 3) eq "(1, 2, 3)"   // [1]
  color(
    red = 76,                     // [2]
    green = 89,
    blue = 0
  ) eq "(76, 89, 0)"
  color(52, 34, blue = 0) eq      // [3]
    "(52, 34, 0)"
}
  • [1] This doesn’t tell you much. You’ll have to look at the documentation to know what the arguments mean.
  • [2] The meaning of every argument is clear.
  • [3] You aren’t required to name all arguments.

Named arguments allow you to change the order of the colors. Here, we specify blue first:

// NamedAndDefaultArgs/ArgumentOrder.kt
import color1.color
import atomictest.eq

fun main() {
  color(blue = 0, red = 99, green = 52) eq
    "(99, 52, 0)"
  color(red = 255, 255, 0) eq
    "(255, 255, 0)"
}

You can mix named and regular (positional) arguments. If you change argument order, you should use named arguments throughout the call—not only for readability, but the compiler often needs to be told where the arguments are.

Named arguments are even more useful when combined with default arguments, which are default values for arguments, specified in the function definition:

// NamedAndDefaultArgs/Color2.kt
package color2
import atomictest.eq

fun color(
  red: Int = 0,
  green: Int = 0,
  blue: Int = 0,
) = "($red, $green, $blue)"

fun main() {
  color(139) eq "(139, 0, 0)"
  color(blue = 139) eq "(0, 0, 139)"
  color(255, 165) eq "(255, 165, 0)"
  color(red = 128, blue = 128) eq
    "(128, 0, 128)"
}

Any argument you don’t provide gets its default value, so you only need to provide arguments that differ from the defaults. If you have a long argument list, this simplifies the resulting code, making it easier to write and—more importantly—to read.

This example also uses a trailing comma in the definition for color(). The trailing comma is the extra comma after the last parameter (blue). This is useful when your parameters or values are written on multiple lines. With a trailing comma, you can add new items and change their order without adding or removing commas.

Named and default arguments (as well as trailing commas) also work for constructors:

// NamedAndDefaultArgs/Color3.kt
package color3
import atomictest.eq

class Color(
  val red: Int = 0,
  val green: Int = 0,
  val blue: Int = 0,
) {
  override fun toString() =
    "($red, $green, $blue)"
}

fun main() {
  Color(red = 77).toString() eq "(77, 0, 0)"
}

joinToString() is a standard library function that uses default arguments. It combines the contents of an iterable (a list, set or range) into a String. You can specify a separator, a prefix element and a postfix element:

// NamedAndDefaultArgs/CreateString.kt
import atomictest.eq

fun main() {
  val list = listOf(1, 2, 3,)
  list.toString() eq "[1, 2, 3]"
  list.joinToString() eq "1, 2, 3"
  list.joinToString(prefix = "(",
    postfix = ")") eq "(1, 2, 3)"
  list.joinToString(separator = ":") eq
    "1:2:3"
}

The default toString() for a List returns the contents in square brackets, which might not be what you want. The default values for joinToString()s parameters are a comma for separator and empty Strings for prefix and postfix. In the above example, we use named and default arguments to specify only the arguments we want to change.

The initializer for list includes a trailing comma. Normally you’ll only use a trailing comma when each element is on its own line.

If you use an object as a default argument, a new instance of that object is created for each invocation:

// NamedAndDefaultArgs/Evaluation.kt
package namedanddefault

class DefaultArg

fun h(d: DefaultArg = DefaultArg()) =
  println(d)

fun main() {
  h()
  h()
}
/* Sample output:
DefaultArg@28d93b30
DefaultArg@1b6d3586
*/

The addresses of the Default objects are different for the two calls to h(), showing that there are two distinct objects.

Specify argument names when they improve readability. Compare the following two calls to joinToString():

// NamedAndDefaultArgs/CreateString2.kt
import atomictest.eq

fun main() {
  val list = listOf(1, 2, 3)
  list.joinToString(". ", "", "!") eq
    "1. 2. 3!"
  list.joinToString(separator = ". ",
    postfix = "!") eq "1. 2. 3!"
}

It’s hard to guess whether ". " or "" is a separator unless you memorize the parameter order, which is impractical.

As another example of default arguments, trimMargin() is a standard library function that formats multi-line Strings. It uses a margin prefix String to establish the beginning of each line. trimMargin() trims leading whitespace characters followed by the margin prefix from every line of the source String. It removes the first and last lines if they are blank:

// NamedAndDefaultArgs/TrimMargin.kt
import atomictest.eq

fun main() {
  val poem = """
    |->Last night I saw upon the stair
        |->A little man who wasn't there
          |->He wasn't there again today
|->Oh, how I wish he'd go away."""
  poem.trimMargin() eq
"""->Last night I saw upon the stair
->A little man who wasn't there
->He wasn't there again today
->Oh, how I wish he'd go away."""
  poem.trimMargin(marginPrefix = "|->") eq
"""Last night I saw upon the stair
A little man who wasn't there
He wasn't there again today
Oh, how I wish he'd go away."""
}

The | (“pipe”) is the default argument for the margin prefix, and you can replace it with a String of your choosing.

Exercises and solutions can be found at www.AtomicKotlin.com.