Skip to content

Combine several flows declaratively #3775

Open
@dovchinnikov

Description

@dovchinnikov

Use case

Combine several flows in a more convenient (and possibly more efficient) way.

We have flows which combine dozens of flows. Existing combine allows to combine at most 5 flows in a type safe way, or to combine an array of flows, but it only works if flows have the same type.

The Shape of the API

fun <T> combine(transform: suspend CombineScope.() -> T): Flow<T> = TODO()

interface CombineScope { // : CoroutineScope ?
  /**
   * Starts collection of [this] flow if not yet started. 
   * Suspends until [this] flow emits the first value.
   * @return the latest emitted value
   */
  suspend fun <T> Flow<T>.latestValue(): T

  /**
   * Starts collection of [this] flow if not yet started.
   * @return the latest emitted value or [initialValue] if [this] flow did not emit anything yet
   */
  suspend fun <T> Flow<T>.latestValue(initialValue: T): T
}

fun usage(ints: Flow<Int>, floats: Flow<Float>): Flow<String> = combine {
  val i = ints.latestValue()
  if (i < 0) {
    return@combine "x"
  }
  else {
    val f = floats.latestValue(initialValue = 2f)
    return@combine f.toString() + i.toString()
  }
}

Pros:

  1. No need to choose type unsafe varargs overload.
  2. Allows to have the variable declaration close to the flow: val i = ints.latestValue().
  3. Allows to avoid collecting unnecessary flows: if the first value from ints is -1, then floats will not be even discovered, and will not be collected until ints emits a value > 0. This can be considered a con (see con №1).
  4. Allows to skip running transform unnecessarily: if both ints and floats are discovered and are being collected, and the latest emitted value from ints was < 0, then all emitted values from floats can be ignored until ints emit a new value, because "x" will be the result of transform regardless of f value.

Cons:

  1. In case of
combine {
  val i = ints.latestValue()
  val f = floats.latestValue() 
}

floats will be started collecting after ints emits a value (see pro №3).

Prior Art

#3598 (comment)
#3598
https://github.com/cashapp/molecule

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions