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.
answer
- returns()/implies = smart-cast facts (null OR type)
- callsInPlace(lambda, InvocationKind) = init analysis
- EXACTLY_ONCE lets you assign a val inside the lambda
- callsInPlace requires inline
- returns() implies (x is Sub) smart-casts to non-null subtype
basics
~20 sKotlin 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 sTwo 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@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
Aware that scope functions like run/let exist and contracts power them, even if not the details.
Distinguishes returns/implies (smart casts) from callsInPlace (init) and knows EXACTLY_ONCE.
Writes a type-check contract that smart-casts to a non-null subtype and explains the inline requirement for callsInPlace.
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