Which Kotlin standard library functions rely on contracts, and what observable behavior do those contracts unlock for callers?
answer
- require/check/assert -> returns implies
- requireNotNull/checkNotNull -> non-null
- isNullOrEmpty/isNullOrBlank -> returns(false) implies
- let/run/with/apply/also -> callsInPlace EXACTLY_ONCE
- repeat -> callsInPlace UNKNOWN
basics
~10 sFunctions like require, check, requireNotNull, isNullOrEmpty, and the scope functions (let, run, also, apply, with, repeat) declare contracts so smart casts and val initialization work cleanly after you call them.
solid answer
~40 sThe stdlib declares contracts on many everyday functions. require/check/assert use returns() implies condition so that after require(x != null) the compiler smart-casts x to non-null. requireNotNull/checkNotNull return the non-null value AND declare returns implies (value != null). String helpers like isNullOrEmpty()/isNullOrBlank() use returns(false) implies (this != null) so an early-return guard smart-casts the receiver. The scope functions (let, run, with, apply, also) and repeat declare callsInPlace(block, EXACTLY_ONCE) so you can initialize a val inside the lambda and the compiler accepts it as definitely assigned. This is why val x: Int; run { x = compute() } compiles. These contracts are the reason idiomatic Kotlin guard clauses propagate type information without manual !! or casts.
code
kotlin · 5 linesfun parse(input: String?): Int {
require(!input.isNullOrBlank()) { "input required" }
// input smart-cast to String thanks to two stdlib contracts
return input.trim().toInt()
}go deeper
Recognizes require and the scope functions behave nicely and names a couple of them.
Maps specific stdlib functions to their effect kind (returns implies vs callsInPlace) and explains the caller benefit.
Explains how guard clauses chain contracts (isNullOrBlank + require) and the EXACTLY_ONCE definite-assignment mechanic.
Reasons about the stability story of stdlib contracts vs the still-experimental author-side DSL and the compatibility implications.
## The stdlib is the main consumer of contracts Contracts were introduced largely so the standard library could make idiomatic Kotlin smarter. The most important examples: ### Precondition functions (returns implies) - `require(condition)`, `check(condition)`: `contract { returns() implies condition }`. After `require(x is Foo)` the compiler smart-casts `x` to `Foo`. - `requireNotNull(value)`, `checkNotNull(value)`: declare `returns() implies (value != null)` and also return the value typed non-null. - `assert(condition)`: same pattern. ### Nullability helpers (conditional returns) - `CharSequence?.isNullOrEmpty()` / `isNullOrBlank()`: `contract { returns(false) implies (this@isNullOrEmpty != null) }`. So: ```kotlin fun greet(name: String?) { if (name.isNullOrEmpty()) return println(name.length) // name smart-cast to String } ``` - Generated `isNullOrEmpty` for collections behaves similarly. ### Scope & control functions (callsInPlace EXACTLY_ONCE) - `let`, `run`, `with`, `apply`, `also`: declare `callsInPlace(block, InvocationKind.EXACTLY_ONCE)`. - `repeat(times) { }`: `callsInPlace(action, InvocationKind.UNKNOWN)`. The EXACTLY_ONCE effect enables definite assignment: ```kotlin val config: Config run { config = loadConfig() // OK: compiler knows run's block runs exactly once } println(config) ``` ## Why callers care Because of these contracts, you can write guard clauses and scope-function initialization that 'just work' — no `!!`, no redundant casts, no 'variable might not have been initialized' errors. Removing the contract would break all that downstream smart-casting even though the function bodies are unchanged. ## Caveat The stdlib contracts themselves were experimental for a long time; they are stable enough to depend on in practice, but the underlying `contract {}` DSL you write yourself is still `@ExperimentalContracts`.
- Why does val x; run { x = ... } compile, but a custom myRun without a contract would not?let/run declare callsInPlace EXACTLY_ONCE, so the compiler proves x is assigned exactly once. A contract-less custom function gives no such guarantee.
- What InvocationKind does repeat use and why not EXACTLY_ONCE?UNKNOWN, because the block may run zero, one, or many times depending on the count argument, so the compiler cannot assume a single assignment.
saying these in an interview costs you the question
- Thinking smart casts after require are compiler magic unrelated to contracts
- Believing let/run special-casing is hardcoded rather than a contract
- Saying repeat uses EXACTLY_ONCE
- Not knowing isNullOrEmpty propagates non-nullability