Implement takeIf yourself and discuss its design: the inline/contract considerations and when its use harms readability.
answer
- inline fun <T> T.takeIf(p): T? = if (p(this)) this else null
- inline -> no lambda allocation, free at runtime
- contract callsInPlace EXACTLY_ONCE enables val init/flow analysis
- Selector family: returns receiver-or-null, not a transform
- Convention: short single-condition gates only; else if/when
basics
~10 stakeIf is a tiny inline extension: if the predicate is true return this, else null. It is inline so the lambda doesn't allocate. Overusing it for complex logic hurts clarity, so prefer if/when there.
solid answer
~40 sA faithful implementation is `inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null`, with `takeUnless` negating the predicate. The stdlib also annotates it with a `callsInPlace(predicate, EXACTLY_ONCE)` contract so the compiler knows the lambda runs exactly once — enabling definite-assignment of `val`s set inside it. `inline` removes the function-object allocation and the call overhead, so it is free at runtime. Design-wise, `takeIf` is a *value selector* that deliberately introduces `T?`, meant to be paired with `?:`/`?.`. Its costs are readability: a non-trivial predicate, several chained gates, or branches producing different shapes all read better as explicit `if`/`when`. As a principal you set the convention: use `takeIf`/`takeUnless` for short, single-condition keep-or-default expressions; switch to control flow when logic grows or when the introduced nullability is incidental rather than intended.
code
kotlin · 11 linesinline fun <T> T.takeIfMine(predicate: (T) -> Boolean): T? =
if (predicate(this)) this else null
inline fun <T> T.takeUnlessMine(predicate: (T) -> Boolean): T? =
if (!predicate(this)) this else null
// inline lets a non-local return work from the predicate:
fun firstReady(nodes: List<Node>): Node? {
nodes.forEach { n -> n.takeIf { it.ready }?.let { return it } }
return null
}go deeper
Can state it returns this-or-null and is a small helper, even if unsure of inline/contracts.
Implements the one-liner correctly and knows it is inline so there's no allocation.
Adds the callsInPlace contract, explains the selector vs transformer family, and the readability limits.
Frames team conventions for when fluent gating aids vs. harms maintainability, grounds it in the trivial inline implementation, and weighs intent-communication over (identical) runtime cost.
## Implementing it The standard-library definitions are essentially: ```kotlin public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (predicate(this)) this else null } public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (!predicate(this)) this else null } ``` Key ingredients: - **Extension on generic `T`** — callable on any value, including nullable receivers (where `this` may be null). - **`inline`** — the lambda body is copied into the call site. No `Function1` object is allocated, no virtual call; non-local `return` from the predicate is even possible. Effectively zero runtime cost. - **`contract { callsInPlace(predicate, EXACTLY_ONCE) }`** — a *Kotlin contract* telling the compiler the lambda is invoked exactly once. This lets you assign a `val` inside the predicate and have the compiler treat it as definitely initialized afterward, and improves smart-cast/flow analysis. ## takeUnless equivalence `x.takeUnless { p }` is exactly `x.takeIf { !p }`. Providing both lets the predicate stay positive and avoids double negatives in reader-facing code. ## Design intent `takeIf`/`takeUnless` are **selectors** in the scope-function family. Unlike `let`/`run` (transformers) and `also`/`apply` (side-effecting, return receiver), selectors return the **receiver or null**. They exist to convert a value-producing `if/else` into a fluent expression, especially when chained with the **Elvis operator `?:`**. ## When it harms readability - **Complex predicates.** A multi-line or side-effecting predicate buried in `takeIf { ... }` is harder to scan than an `if` block. - **Stacked gates.** `a.takeIf{}?.takeUnless{}?.takeIf{}` layers nullability and obscures the actual condition; collapse into one predicate or use `when`. - **Incidental nullability.** If you don't actually want a `null` outcome, introducing one just to use `takeIf` is a smell. - **Different result shapes.** When the two branches produce different things, `if`/`when` makes the divergence explicit; `takeIf` only ever yields the receiver-or-null. ## Governance angle As a principal/lead the value is in **codifying the convention**: encourage `takeIf`/`takeUnless` for terse single-condition keep-or-default expressions; require explicit control flow once predicates grow, several gates stack, or reviewers stumble. The runtime is identical either way (`inline`), so the decision is purely about communicating intent. ## Bonus: relation to standard if Because `takeIf` is just `if (predicate(this)) this else null`, there is nothing magic — it is sugar. Knowing this demystifies it and helps reason about edge cases (e.g., the predicate sees the possibly-null `this` when called on a nullable receiver without `?.`).
- What does the callsInPlace(EXACTLY_ONCE) contract buy the caller?It tells the compiler the predicate runs exactly once, so a val assigned inside it is treated as definitely initialized afterward and flow/smart-cast analysis works through the call.
- Why is takeIf essentially free at runtime?It's inline, so the lambda is inlined at the call site with no Function object allocation and no extra call — it compiles to a plain if-expression.
saying these in an interview costs you the question
- Thinking takeIf is a special compiler intrinsic rather than a one-line inline function
- Believing it allocates a lambda object each call
- Defending deeply chained takeIf as always cleaner than when
- Introducing null just to use takeIf when no null outcome is wanted
- Not knowing takeUnless is just takeIf with a negated predicate