Explain how `++`, `--`, and augmented assignments (`+=`) are resolved via operator conventions, including the `plus` vs `plusAssign` ambiguity.
answer
- ++ -> inc(), -- -> dec(), return same type, var required
- Prefix/postfix handled by compiler, not your function
- += tries plusAssign first, else a = a.plus(b)
- Both plus + plusAssign -> ambiguity error
- plusAssign for mutable, plus for value types
basics
~20 s++ uses inc() and -- uses dec(), each returning a new value. a += b first tries plusAssign (mutating); if absent it falls back to a = a + b using plus. Defining both can cause an ambiguity error.
solid answer
~40 sIncrement/decrement map to `operator fun inc()` / `dec()`, which must return the receiver's type; the compiler handles prefix vs postfix semantics and reassigns the variable. Augmented assignments have a two-path resolution: `a += b` prefers `a.plusAssign(b)` (an in-place, `Unit`-returning mutation) when available; otherwise it rewrites to `a = a.plus(b)`. If a type defines **both** `plus` and `plusAssign` and `a` is a `var`, the call is **ambiguous** and the compiler reports an error. Convention: define `plusAssign` (and `minusAssign`, `timesAssign`, `divAssign`, `remAssign`) on **mutable** containers (e.g. `MutableList`) and `plus` on **immutable/value** types that return new instances. For a `val`, only `plusAssign` is viable since reassignment isn't possible.
code
kotlin · 11 lines// Value type: plus -> += reassigns
data class Vec(val x: Int, val y: Int) {
operator fun plus(o: Vec) = Vec(x + o.x, y + o.y)
}
var v = Vec(1, 1); v += Vec(2, 2) // v = v.plus(...) -> Vec(3,3)
// Mutable type: plusAssign -> += mutates
class Bag(val items: MutableList<Int> = mutableListOf()) {
operator fun plusAssign(item: Int) { items.add(item) }
}
val bag = Bag(); bag += 5 // bag.plusAssign(5)go deeper
Knows ++ increments and += adds-and-assigns conceptually.
States inc/dec names and that += can use plus; may miss plusAssign nuance.
Explains the plusAssign-vs-plus resolution order, the ambiguity error, and the mutable-vs-value design split; knows inc returns a value the compiler reassigns.
Sets library conventions (plusAssign on mutable receivers, plus on value types) and reasons about how stdlib avoids the clash via distinct receiver types.
## Increment and decrement: `inc` / `dec` ``` a++ and ++a -> use a.inc() a-- and --a -> use a.dec() ``` - `operator fun inc(): T` and `operator fun dec(): T` must **return the same type** (the receiver type or a subtype). - They are **not mutating** by themselves: the compiler takes the returned value and **reassigns the variable**, so `a` must be a `var` to use `++`/`--`. - **Prefix vs postfix** is handled by the compiler: `++a` evaluates to the new value; `a++` evaluates to the old value, then stores the new one. Your `inc()` just computes the next value. ```kotlin data class Counter(val n: Int) { operator fun inc() = Counter(n + 1) } var c = Counter(0) c++ // c becomes Counter(1) ``` ## Augmented assignment: two resolution paths For each arithmetic operator there are **two** relevant conventions: | Operator | Value-returning | In-place (mutating) | |----------|-----------------|---------------------| | `+=` | `plus` | `plusAssign` | | `-=` | `minus` | `minusAssign` | | `*=` | `times` | `timesAssign` | | `/=` | `div` | `divAssign` | | `%=` | `rem` | `remAssign` | Resolution of `a += b`: 1. If `a.plusAssign(b)` exists (returns `Unit`, mutates `a` in place), use it. 2. Else rewrite to `a = a.plus(b)` (requires `a` to be a `var`). ```kotlin val list = mutableListOf(1, 2) list += 3 // MutableList has plusAssign -> mutates in place var v = Vec(1, 1) v += Vec(2, 2) // Vec only has plus -> v = v.plus(Vec(2,2)) ``` ## The `plus` + `plusAssign` ambiguity If a `var` type defines **both** `plus` and `plusAssign` with matching signatures, then `a += b` is **ambiguous** — the compiler can't decide between mutating (`plusAssign`) and reassigning (`a = a + b`) and emits a **compile error**. ```kotlin class Bad { operator fun plus(x: Int): Bad = TODO() operator fun plusAssign(x: Int) { TODO() } } var b = Bad() // b += 1 // ERROR: ambiguity between 'plus' and 'plusAssign' ``` ### Design rule of thumb - **Immutable / value types** (Money, Vec): define `plus`, return a new instance. `+=` reassigns. - **Mutable containers** (collections, builders): define `plusAssign`, mutate in place. Don't also define `plus` returning the same type if you want `+=` unambiguous (the stdlib gives `MutableCollection` a `plusAssign` while `Collection.plus` returns a new list — they live on different receiver types, avoiding the clash). - For a **`val`**, `a += b` can only use `plusAssign` (no reassignment possible). ## Summary Increment/decrement are pure value computations the compiler reassigns; augmented assignment has a mutate-or-reassign duality whose ambiguity you avoid by choosing `plusAssign` for mutable types and `plus` for value types — never both on the same receiver.
- Does your `inc()` function need to mutate the variable itself?No — it returns the next value; the compiler reassigns the variable, which is why the variable must be a `var`.
- Why can `val list = mutableListOf(...)` still support `list += x`?Because `MutableList` provides `plusAssign`, which mutates in place — no reassignment of the `val` is needed.
- What error appears if a type has both `plus` and `plusAssign`?An ambiguity/assignment-operator error: the compiler cannot choose between `a = a + b` and `a.plusAssign(b)`.
saying these in an interview costs you the question
- Saying `inc()` must mutate state itself
- Claiming postfix/prefix logic must be coded in your function
- Not knowing `+=` prefers `plusAssign` over `plus`
- Defining both plus and plusAssign and expecting it to compile
- Thinking `++` works on a `val`