skip to content

Beyond null smart-casts, what other contract effects exist, and how do returns()/implies differ from callsInPlace? Give a contract that smart-casts to a non-null subtype.

level: seniorimportance: nice to knowfreq 18%

answer

  1. returns()/implies = smart-cast facts (null OR type)
  2. callsInPlace(lambda, InvocationKind) = init analysis
  3. EXACTLY_ONCE lets you assign a val inside the lambda
  4. callsInPlace requires inline
  5. returns() implies (x is Sub) smart-casts to non-null subtype

basics

~20 s

Kotlin contracts have two families: returns()/implies, which tells the compiler facts about arguments (like 'not null' or 'is this type') after a result, and callsInPlace, which tells it how often a lambda runs so you can initialize vals inside it.

solid answer

~40 s

Two effect kinds: (1) Conditional 'returns effects' — returns(), returns(true|false|null) combined with implies (condition). The condition can be a null check OR a type check (x is Sub), so a single helper can smart-cast to a non-null subtype, e.g. contract { returns() implies (node is TextNode) }. (2) callsInPlace(lambda, InvocationKind) — declares that a passed lambda is invoked in place EXACTLY_ONCE / AT_MOST_ONCE / AT_LEAST_ONCE / UNKNOWN. EXACTLY_ONCE is what lets val x: T; run { x = ... } and inline scoping functions (run, let, also, apply, with, repeat) allow definite-assignment of vals and not flag 'captured before initialized'. callsInPlace requires the function to be inline. The two are orthogonal: returns/implies enables smart casts; callsInPlace enables definite-assignment/initialization analysis. Both live in the same contract { } block and both are experimental.

code

kotlin · 11 lines
kotlin
@OptIn(ExperimentalContracts::class)
inline fun <R> once(block: () -> R): R {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    return block()
}

fun demo() {
    val v: Int
    once { v = 1 } // val assignment allowed thanks to EXACTLY_ONCE
    println(v)
}

go deeper

for a junior

Aware that scope functions like run/let exist and contracts power them, even if not the details.

for a middle

Distinguishes returns/implies (smart casts) from callsInPlace (init) and knows EXACTLY_ONCE.

for a senior

Writes a type-check contract that smart-casts to a non-null subtype and explains the inline requirement for callsInPlace.

for a principal

Reasons about exposing such contract-bearing APIs, the experimental-stability cost, and how stdlib scope functions rely on EXACTLY_ONCE.

## Two contract effect families ### 1. Returns effects + implies (drives smart casts) ```kotlin contract { returns() implies (x != null) } // null smart cast contract { returns(true) implies (x is TextNode) } // type smart cast on true contract { returns(false) implies (x != null) } // non-null on false (isNullOrEmpty) ``` The `implies` condition isn't limited to nullability — a **type check** lets the helper smart-cast to a non-null **subtype**: ```kotlin sealed interface Node class TextNode(val text: String) : Node class ImageNode(val url: String) : Node @OptIn(ExperimentalContracts::class) fun requireText(node: Node?) { contract { returns() implies (node is TextNode) } require(node is TextNode) { "expected text" } } fun use(n: Node?) { requireText(n) println(n.text) // n smart-cast to TextNode (non-null subtype) } ``` ### 2. callsInPlace (drives initialization / definite-assignment) ```kotlin @OptIn(ExperimentalContracts::class) inline fun <R> runOnce(block: () -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block() } fun demo() { val v: Int runOnce { v = 42 } // allowed: compiler knows block runs exactly once println(v) // v is definitely assigned } ``` `InvocationKind` values: - **EXACTLY_ONCE** — exactly one call (enables assigning a `val` inside, treats it as definitely initialized). - **AT_MOST_ONCE** — zero or one. - **AT_LEAST_ONCE** — one or more. - **UNKNOWN** — default if unspecified. This is why `run`, `let`, `also`, `apply`, `with`, and `repeat` in the stdlib let you initialize `val`s and don't complain about capturing an uninitialized variable. `callsInPlace` **requires the function to be `inline`**. ## How they differ | Aspect | returns()/implies | callsInPlace | |---|---|---| | Purpose | Smart-cast facts about args after a result | Lambda invocation count for init analysis | | Enables | `x` usable as non-null / subtype | `val` assignment inside lambda, definite-assignment | | Requires inline? | Not strictly | Yes | | Condition forms | null/type/boolean on params | invocation kind on a lambda param | Both are declared inside the same `contract { }` first-statement block and both need `@OptIn(ExperimentalContracts::class)`.

  • Which InvocationKind lets you assign a val inside the lambda, and why?
    EXACTLY_ONCE — the compiler knows the lambda runs once and only once, so the val is definitely assigned exactly one time, satisfying definite-assignment rules.
  • Can a returns()/implies contract smart-cast to something other than non-null?
    Yes — with a type check like returns() implies (x is TextNode), it smart-casts to that (non-null) subtype, not just to the non-null version of the same type.

saying these in an interview costs you the question

  • Thinking implies only supports null checks, not type checks
  • Claiming callsInPlace works on non-inline functions
  • Confusing callsInPlace (init analysis) with returns/implies (smart casts)
  • Saying AT_MOST_ONCE allows val assignment inside the lambda (it doesn't guarantee a single assignment)
  • Believing the two effects can't coexist in one contract block

context