What are flattenConcat/flattenMerge, how do they relate to the flatMap* operators, and what should you know about the experimental/stability status of these operators?
answer
- flatMap* = map + flatten*
- flattenConcat sequential, flattenMerge concurrent
- No flattenLatest exists
- Opt-in: @FlowPreview / @ExperimentalCoroutinesApi
- Verify stability per coroutines version
basics
~10 sflattenConcat and flattenMerge flatten an existing Flow<Flow<T>>. flatMapConcat/Merge are just map-then-flatten shortcuts. Some of these operators were experimental and need an opt-in annotation; check your kotlinx-coroutines version.
solid answer
~30 sIf you already have a Flow<Flow<T>> (e.g. from map), flatten it directly with flattenConcat() (sequential) or flattenMerge(concurrency) (concurrent). The flatMap* operators are conveniences: flatMapConcat == map{...}.flattenConcat(); flatMapMerge == map{...}.flattenMerge(concurrency); flatMapLatest == transformLatest { emitAll(transform(it)) }. Historically several Flow operators were marked with @FlowPreview or @ExperimentalCoroutinesApi, requiring an opt-in annotation (@OptIn) or compiler flag, and signatures could change between versions. Treat the exact stability as version-dependent: verify in the kotlinx-coroutines version you ship, and add the opt-in if the compiler asks. There is no flattenLatest; the 'latest' family is expressed via flatMapLatest/transformLatest. Prefer the stable, current operator and consult your version's API.
code
kotlin · 9 linesimport kotlinx.coroutines.flow.*
val ids = flowOf(1, 2, 3)
fun load(id: Int) = flowOf("a$id", "b$id")
// flatMapConcat is sugar over map + flattenConcat
val viaFlatMap = ids.flatMapConcat { load(it) }
val viaFlatten = ids.map { load(it) }.flattenConcat()
// both yield: a1,b1,a2,b2,a3,b3go deeper
Knows flatMap* flattens nested flows and that flatten* operate on an existing Flow<Flow<T>>.
States the map+flatten equivalences and that there is no flattenLatest.
Explains opt-in annotations (@FlowPreview/@ExperimentalCoroutinesApi), @OptIn, and version-dependent stability.
Sets team policy on opt-in usage, version pinning, and migration when experimental operators stabilise or change signatures.
## flatten* vs flatMap* When you already hold a `Flow<Flow<T>>`, use the flatten operators: - `flattenConcat()` — sequential, like concat. - `flattenMerge(concurrency = DEFAULT_CONCURRENCY)` — concurrent, like merge. The `flatMap*` operators are defined in terms of map + flatten (or transform): ```kotlin // Conceptual equivalences flow.flatMapConcat(transform) == flow.map(transform).flattenConcat() flow.flatMapMerge(c, transform) == flow.map(transform).flattenMerge(c) flow.flatMapLatest(transform) == flow.transformLatest { emitAll(transform(it)) } ``` There is **no** `flattenLatest`; the 'latest' semantics live only in `flatMapLatest` / `mapLatest` / `transformLatest`. ```kotlin import kotlinx.coroutines.flow.* val nested: Flow<Flow<Int>> = flowOf(flowOf(1, 2), flowOf(3, 4)) val seq = nested.flattenConcat() // 1,2,3,4 ordered val par = nested.flattenMerge(concurrency = 2) // interleaved ``` ## Stability / opt-in annotations Kotlin marks not-yet-stable APIs with **opt-in annotations**: - `@FlowPreview` and `@ExperimentalCoroutinesApi` have historically guarded various Flow operators. - Using such an API triggers a compiler warning/error unless you opt in: annotate the call site with `@OptIn(FlowPreview::class)` (or the relevant marker), or set the `-opt-in` compiler flag. - Experimental signatures may change between kotlinx-coroutines releases, so pin and verify your version. ```kotlin @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) fun pipeline(f: Flow<Int>) = f.flatMapLatest { fetch(it) } ``` ## Practical guidance - Don't memorise a fixed 'this is experimental' list; the status changes across versions. **Check the version you ship.** - If the compiler complains, add the requested `@OptIn` rather than suppressing all warnings. - Prefer the documented stable operator for the semantics you need (concat/merge/latest) and let map+flatten be an implementation detail you can fall back to. ## Key terms - **Opt-in annotation**: a marker (e.g. `@ExperimentalCoroutinesApi`) requiring explicit `@OptIn` acknowledgement to use an unstable API. - **@FlowPreview / @ExperimentalCoroutinesApi**: kotlinx-coroutines stability markers. - **flattenConcat / flattenMerge**: flatten an existing Flow<Flow<T>>.
- Is there a flattenLatest operator?No. 'Latest' semantics are only available through flatMapLatest / mapLatest / transformLatest.
- How do you satisfy the compiler when calling an experimental Flow operator?Add @OptIn(<MarkerAnnotation>::class) at the call site/function, or set the -opt-in compiler argument; don't blanket-suppress warnings.
saying these in an interview costs you the question
- Asserting a fixed experimental status without naming the version
- Inventing a flattenLatest operator
- Suppressing all warnings instead of targeted @OptIn
- Thinking flatMapConcat is fundamentally different from map+flattenConcat
- Confusing flattenMerge's concurrency default with something other than 16