Walk through the opt-in mechanics for the contracts DSL: what makes it experimental, how do you opt in at function vs module level, and what does that imply for a published API?
answer
- @ExperimentalContracts is a @RequiresOptIn marker
- @OptIn on function/file vs -opt-in module flag
- @OptIn is non-propagating
- Callers need no opt-in
- Stdlib ships contracts despite experimental status
basics
~20 sThe contract { } function is marked experimental, so the compiler warns or errors unless you opt in. You can opt in on one function with @OptIn, or for the whole module with a compiler flag. Either way you accept that the API could change.
solid answer
~40 skotlin.contracts.contract is annotated @ExperimentalContracts, which is itself a @RequiresOptIn marker. The opt-in system means any usage requires explicit acknowledgement: annotate the declaring function (or an enclosing element) with @OptIn(ExperimentalContracts::class), or opt in module-wide via the Gradle compiler argument -opt-in=kotlin.contracts.ExperimentalContracts (e.g. compilerOptions.optIn.add(...)). @OptIn is propagation-free — it does not force callers to opt in, because using a contracted function is not itself using the experimental API; only authoring the contract is. So a library can ship contracted functions and consumers see normal smart-cast behavior without any opt-in of their own. The implication: you accept that the contracts DSL surface is unstable across Kotlin versions, even though stdlib has used it in stable releases for years.
code
kotlin · 7 lines// Module-wide opt-in in build.gradle.kts:
kotlin {
compilerOptions {
optIn.add("kotlin.contracts.ExperimentalContracts")
}
}
// Now no per-function @OptIn is needed to declare contract { }go deeper
Knows you need @OptIn(ExperimentalContracts::class) on the function.
Can opt in at both function and module level and knows the marker name.
Explains that @OptIn is non-propagating so callers are unaffected, and the author bears the experimental risk.
Sets module policy (per-use vs module flag), reasons about cross-version DSL drift, and the stable-stdlib-vs-experimental-DSL tension for a published library.
## What makes it experimental The top-level `contract { }` function lives in `kotlin.contracts` and is annotated `@ExperimentalContracts`. That annotation is declared with `@RequiresOptIn`, Kotlin's general mechanism for marking APIs whose stability is not guaranteed. Touching such an API without acknowledgement produces a compiler error (or warning, depending on the marker's level). ## Two ways to opt in ### Function / declaration level ```kotlin import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @OptIn(ExperimentalContracts::class) fun isNonNull(x: Any?): Boolean { contract { returns(true) implies (x != null) } return x != null } ``` `@OptIn(...)` placed on the function (or its class/file) acknowledges the experimental usage for that scope. ### Module level (Gradle) ```kotlin // build.gradle.kts kotlin { compilerOptions { optIn.add("kotlin.contracts.ExperimentalContracts") } } ``` or the raw flag `-opt-in=kotlin.contracts.ExperimentalContracts`. This opts in every file in the module so you do not repeat `@OptIn`. ## Opt-in does not propagate to callers This is the key subtlety. `@OptIn` is **non-propagating**: it does not annotate your function as experimental. Only **declaring** a contract uses the experimental API; **calling** a function that happens to have a contract does not. Therefore: - A library can publish functions that declare contracts. - Consumers call them and enjoy the smart casts / definite assignment **without** any opt-in. - The experimental obligation stays entirely on the author. Contrast with `@RequiresOptIn` markers you might define yourself and want to propagate — for those you'd use `@MyMarker` on the API instead of `@OptIn`. ## Implications for a published API - You are taking on the risk that the contracts DSL syntax could change between Kotlin versions; you'd then have to update your declarations (your public signatures and runtime behavior stay the same). - Because the obligation does not leak to consumers, shipping contracts is low-friction for them. - The stdlib itself opts in internally and ships contracts in stable releases, which is strong evidence the feature is dependable in practice even while formally experimental. ## Quick reference - Marker: `kotlin.contracts.ExperimentalContracts` (`@RequiresOptIn`). - Per-use: `@OptIn(ExperimentalContracts::class)`. - Module: `-opt-in=kotlin.contracts.ExperimentalContracts`. - Propagation: none — callers unaffected.
- If my library function declares a contract, must consumers also opt in?No. @OptIn is non-propagating and calling a contracted function is not an experimental usage, so consumers need nothing.
- What is the difference between @OptIn and propagating a @RequiresOptIn marker?@OptIn acknowledges and contains the experimental usage locally; annotating the API with the marker itself would force callers to opt in. Contracts use the former.
saying these in an interview costs you the question
- Claiming callers must opt in to use contracted functions
- Confusing @OptIn (non-propagating) with re-annotating the API as experimental
- Not knowing the module-level -opt-in flag exists
- Thinking experimental means the feature is unusable in production