Skip to content

Commit

Permalink
Introduce runningFold operator, make scan alias to runningFold (Kotli…
Browse files Browse the repository at this point in the history
…n#2645)

* Otherwise Kotlin users with non-reactive background get confused by flow/stdlib inconsistency
    * Make it experimental to delay the final decision about the name

Fixes Kotlin#2641
  • Loading branch information
qwwdfsad authored and pablobaxter committed Sep 14, 2022
1 parent 954503e commit 1f2254d
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 2 deletions.
1 change: 1 addition & 0 deletions kotlinx-coroutines-core/api/kotlinx-coroutines-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,7 @@ public final class kotlinx/coroutines/flow/FlowKt {
public static synthetic fun retry$default (Lkotlinx/coroutines/flow/Flow;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun retry$default (Lkotlinx/coroutines/flow/Flow;JLkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
public static final fun retryWhen (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function4;)Lkotlinx/coroutines/flow/Flow;
public static final fun runningFold (Lkotlinx/coroutines/flow/Flow;Ljava/lang/Object;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow;
public static final fun runningReduce (Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/flow/Flow;
public static final fun sample (Lkotlinx/coroutines/flow/Flow;J)Lkotlinx/coroutines/flow/Flow;
public static final fun sample-HG0u8IE (Lkotlinx/coroutines/flow/Flow;D)Lkotlinx/coroutines/flow/Flow;
Expand Down
18 changes: 16 additions & 2 deletions kotlinx-coroutines-core/common/src/flow/operators/Transform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@ public fun <T> Flow<T>.onEach(action: suspend (T) -> Unit): Flow<T> = transform
return@transform emit(value)
}

/**
* Folds the given flow with [operation], emitting every intermediate result, including [initial] value.
* Note that initial value should be immutable (or should not be mutated) as it is shared between different collectors.
* For example:
* ```
* flowOf(1, 2, 3).scan(emptyList<Int>()) { acc, value -> acc + value }.toList()
* ```
* will produce `[], [1], [1, 2], [1, 2, 3]]`.
*
* This function is an alias to [runningFold] operator.
*/
@ExperimentalCoroutinesApi
public fun <T, R> Flow<T>.scan(initial: R, @BuilderInference operation: suspend (accumulator: R, value: T) -> R): Flow<R> = runningFold(initial, operation)

/**
* Folds the given flow with [operation], emitting every intermediate result, including [initial] value.
* Note that initial value should be immutable (or should not be mutated) as it is shared between different collectors.
Expand All @@ -84,7 +98,7 @@ public fun <T> Flow<T>.onEach(action: suspend (T) -> Unit): Flow<T> = transform
* will produce `[], [1], [1, 2], [1, 2, 3]]`.
*/
@ExperimentalCoroutinesApi
public fun <T, R> Flow<T>.scan(initial: R, @BuilderInference operation: suspend (accumulator: R, value: T) -> R): Flow<R> = flow {
public fun <T, R> Flow<T>.runningFold(initial: R, @BuilderInference operation: suspend (accumulator: R, value: T) -> R): Flow<R> = flow {
var accumulator: R = initial
emit(accumulator)
collect { value ->
Expand All @@ -100,7 +114,7 @@ public fun <T, R> Flow<T>.scan(initial: R, @BuilderInference operation: suspend
*
* For example:
* ```
* flowOf(1, 2, 3, 4).runningReduce { (v1, v2) -> v1 + v2 }.toList()
* flowOf(1, 2, 3, 4).runningReduce { acc, value -> acc + value }.toList()
* ```
* will produce `[1, 3, 6, 10]`
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ class ScanTest : TestBase() {
assertEquals(listOf(emptyList(), listOf(1), listOf(1, 2), listOf(1, 2, 3)), result)
}

@Test
fun testFoldWithInitial() = runTest {
val flow = flowOf(1, 2, 3)
val result = flow.runningFold(emptyList<Int>()) { acc, value -> acc + value }.toList()
assertEquals(listOf(emptyList(), listOf(1), listOf(1, 2), listOf(1, 2, 3)), result)
}

@Test
fun testNulls() = runTest {
val flow = flowOf(null, 2, null, null, null, 5)
Expand Down

0 comments on commit 1f2254d

Please sign in to comment.