skip to content

How do `operator` and `infix` functions extend the function model in Kotlin? Give the rules and a concrete example of each.

level: seniorimportance: should knowfreq 45%

answer

  1. operator: fixed symbol set, reserved names
  2. plus/get/set/invoke/contains/compareTo/rangeTo
  3. infix: member/extension, exactly one param
  4. no vararg, no default for infix param
  5. `to` builds a Pair; pure compile-time sugar

basics

~20 s

operator lets a function back a symbol like +, [], or in, so a + b calls a.plus(b). infix lets you call a one-parameter function without the dot and parentheses, like 1 to 2. Both are syntax sugar over normal functions.

solid answer

~50 s

Kotlin maps certain **symbols and call styles** onto ordinary functions. Marking a function `operator` with a recognized name makes a symbol delegate to it: `plus` -> `+`, `get`/`set` -> `[]`, `invoke` -> `()`, `compareTo` -> `< > <= >=`, `contains` -> `in`, `rangeTo` -> `..`, `inc`/`dec` -> `++`/`--`. The set of overloadable operators is **fixed** — you can't invent new symbols. An **`infix`** function can be called as `a fn b` (no dot/parentheses); requirements: it must be a **member or extension**, take **exactly one** parameter, and that parameter can't be `vararg` or have a default. `to` (creating a `Pair`) is the canonical infix example. Both features are pure sugar resolved at compile time — they don't change dispatch — and should be used only when the symbol/word reads naturally (math types, DSLs); overusing them harms clarity.

code

kotlin · 9 lines
kotlin
class Matrix(private val rows: List<List<Int>>) {
    operator fun get(r: Int, c: Int) = rows[r][c]        // m[r, c]
    operator fun plus(o: Matrix) = Matrix(
        rows.mapIndexed { i, row -> row.mapIndexed { j, v -> v + o[i, j] } }
    )
}

infix fun Int.upToBy(step: Int): IntProgression = this..(this + 10) step step
for (i in 0 upToBy 2) print("$i ")   // 0 2 4 6 8 10

go deeper

for a junior

Recognizes a + b and a to b as function calls and can name plus/to.

for a middle

Can declare an operator fun and an infix fun and state the infix constraints.

for a senior

Knows the full reserved-name mapping (get/set/invoke/contains/compareTo), precedence, and the closed operator set.

for a principal

Judges API ergonomics vs. readability, DSL design with invoke/infix, and the maintenance cost of symbolic APIs across a team.

## operator functions Kotlin lets specific symbols be **backed by functions** with reserved names, marked `operator`. The compiler rewrites the symbol into a call: | Expression | Calls | |---|---| | `a + b` | `a.plus(b)` | | `a - b` | `a.minus(b)` | | `a * b` | `a.times(b)` | | `a[i]` | `a.get(i)` | | `a[i] = v` | `a.set(i, v)` | | `a()` | `a.invoke()` | | `a in c` | `c.contains(a)` | | `a..b` | `a.rangeTo(b)` | | `a < b` etc. | `a.compareTo(b)` | | `a += b` | `a.plusAssign(b)` or `a = a.plus(b)` | The set of operators is **closed** — you can overload existing symbols but cannot define brand-new ones. ```kotlin data class Vec(val x: Int, val y: Int) { operator fun plus(o: Vec) = Vec(x + o.x, y + o.y) } Vec(1, 2) + Vec(3, 4) // Vec(4, 6) via plus ``` `invoke` is especially powerful: defining `operator fun invoke(...)` makes an object callable like a function, which underpins DSLs and function-object patterns. ## infix functions An **infix function** can be called without the dot and parentheses: `a fn b` instead of `a.fn(b)`. Rules: - Must be a **member function** or an **extension function**. - Must take **exactly one** parameter. - The parameter must **not** be `vararg` and must **not** have a default value. - Must be marked with the `infix` keyword. ```kotlin infix fun Int.times(str: String) = str.repeat(this) 2 times "ab" // "abab" ``` The standard library's `to` is infix and builds a `Pair`: ```kotlin val p = "key" to 42 // Pair("key", 42) mapOf("a" to 1, "b" to 2) ``` ### Precedence note Infix calls have **lower precedence than arithmetic** but higher than comparison/boolean operators, so `1 + 2 shl 3` parses as `(1 + 2) shl 3`. When mixing with operators, parenthesize for clarity. ## How this fits the function model Both are **compile-time sugar**: the symbol or infix word is just another way to spell a normal function call. Dispatch, overload resolution, and types behave exactly as for the underlying function. They don't add runtime cost beyond the call itself. ## When to use - `operator`: numeric/value types (vectors, money, ranges), collection-like access (`get`/`set`), callable objects (`invoke`). - `infix`: builder/DSL words that read like English (`to`, `until`, `downTo`, custom matchers). - **Avoid** when the symbol/word obscures intent — `+` on a non-additive type is a classic readability trap.

  • Can you define a brand-new operator symbol like `<>` in Kotlin?
    No. The set of overloadable operators is fixed; you can only give meaning to existing symbols via their reserved function names (plus, times, get, ...).
  • What three constraints must an infix function satisfy?
    It must be a member or extension, take exactly one parameter, and that parameter must not be vararg or have a default value (and it must be marked `infix`).

operator and infix are nicknames for functions: a + b and a to b are friendly spellings of a.plus(b) and a.to(b) — same person, easier to address.

saying these in an interview costs you the question

  • Claiming you can invent new operator symbols
  • Forgetting infix needs exactly one non-default, non-vararg parameter
  • Saying operator overloading changes dispatch or adds hidden cost
  • Overloading + on a type where addition makes no semantic sense
  • Not knowing `to` is just an infix extension building a Pair

context