Explain the four `InvocationKind` values used with `callsInPlace` and what each one permits the compiler to assume about a `val` (and a `var`) assigned inside the lambda.
answer
- EXACTLY_ONCE: val assign + read after
- AT_LEAST_ONCE: read after yes, val assign no (use var)
- AT_MOST_ONCE: val assign yes, read after no
- UNKNOWN: only 'in place', weakest
- all kinds imply synchronous, non-escaping call
basics
~20 sThe kind says how often the lambda runs: exactly once, at most once (zero or one), at least once (one or more), or unknown. Only 'exactly once' lets you assign a val and use it afterward as initialized.
solid answer
~40 s`InvocationKind` has four members. `EXACTLY_ONCE`: the lambda runs precisely one time — a `val` assigned inside is then definitely initialized and assigned-exactly-once after the call. `AT_MOST_ONCE`: zero or one time — a `val` may be assigned, but the compiler cannot guarantee initialization afterward, so reading it later still fails unless otherwise set. `AT_LEAST_ONCE`: one or more times — it is definitely initialized afterward, but you may **not** assign a `val` inside (could run twice, violating single-assignment); a `var` is fine and is definitely initialized. `UNKNOWN`: no count guarantee — only asserts the lambda is called in place (synchronously), enabling smart-cast/initialization reasoning that doesn't depend on count, but neither val assignment nor guaranteed-initialized reads. All four still imply the lambda is invoked in place, i.e., not stored or deferred.
go deeper
Recognizes EXACTLY_ONCE as the one that 'lets you set a val'; may not know the other three precisely.
Correctly maps all four kinds to val-assignability and post-call initialization, and knows the 'in place' commonality.
Explains why AT_LEAST_ONCE forbids val assignment while still guaranteeing initialization, and uses a var as the workaround.
Reasons about the lattice of guarantees, where UNKNOWN fits, and how the analysis composes with nested scope functions and exceptions.
## InvocationKind enum `kotlin.contracts.InvocationKind` has exactly four values. The shared meaning of `callsInPlace(block, kind)` is: the lambda is **called in place** — synchronously, within the function call, never escaping to be run later/elsewhere. The `kind` refines *how many times*. | Kind | Times | val assignable inside? | val/var initialized after? | |------|-------|------------------------|----------------------------| | `EXACTLY_ONCE` | exactly 1 | yes | yes | | `AT_LEAST_ONCE` | 1..n | no (might run twice → double-assign) | yes | | `AT_MOST_ONCE` | 0 or 1 | yes (still ≤ once) | no (might run 0 times) | | `UNKNOWN` | any | no | no | ## Why each rule holds - **EXACTLY_ONCE** is the sweet spot: single assignment + guaranteed run ⇒ both "assign a val" and "read it after" are legal. This is what `run`, `let`, `with`, `also`, `apply` use. - **AT_LEAST_ONCE** guarantees initialization (it ran), but because it might run *more* than once, assigning a `val` inside would risk a second assignment — illegal. Use a `var` instead, or just rely on "initialized after". `repeat` uses this kind. - **AT_MOST_ONCE** allows the `val` assignment (never more than once), but since it might run *zero* times, the compiler cannot prove the value is set afterward — reading it post-call still requires it to be definitely assigned by some other path. - **UNKNOWN** gives the weakest guarantee: only "in place". It is mostly useful so a contract author can still promise the lambda doesn't escape (e.g., for other flow facts) without committing to a count. ## Code illustration ```kotlin @OptIn(ExperimentalContracts::class) inline fun atMostOnce(block: () -> Unit) { contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } if (Random.nextBoolean()) block() } val x: Int atMostOnce { x = 1 } // OK to assign (<= once) // println(x) // ERROR: might not be initialized (could run 0 times) @OptIn(ExperimentalContracts::class) inline fun atLeastOnce(block: () -> Unit) { contract { callsInPlace(block, InvocationKind.AT_LEAST_ONCE) } do { block() } while (Random.nextBoolean()) } var y: Int atLeastOnce { y = 1 } // var, fine println(y) // OK: definitely initialized (ran >= once) ``` ## Term reminders - **In place / does not escape**: not stored in a field, not passed to another thread, not run after the function returns. - **Definite assignment**: compiler proof a variable is set before read. - The default when you call `callsInPlace(block)` without a kind is `UNKNOWN`.
- Which kind does `repeat` use and why not EXACTLY_ONCE?`AT_LEAST_ONCE` is not quite right either — `repeat(n)` runs the action n times where n could be 0, so it actually cannot promise EXACTLY_ONCE; in practice its body runs 0..n. The key point: only EXACTLY_ONCE permits val assignment, which is why repeat doesn't enable it.
- If a function might call the lambda 0 or 1 times, why allow val assignment at all?Because single-assignment is still satisfied (never twice). The trade-off is the compiler can't prove the val is initialized afterward, so post-call reads remain restricted.
saying these in an interview costs you the question
- Says AT_LEAST_ONCE allows val assignment (it doesn't — may run twice)
- Says AT_MOST_ONCE guarantees the value is set afterward
- Forgets that all kinds imply 'called in place / not escaping'
- Thinks UNKNOWN is useless or means 'never called'
- Confuses invocation count with the value the lambda returns