Skip to content

Extensions for Kotlin Flow? #78

@curioustechizen

Description

@curioustechizen

Have you considered providing extensions for Kotlin Flows, specifically, Flow<Result<V, E>>? I'm making heavy use of these "flows-of-result" and I have some helpers of my own. However, I feel that it would be much nicer to have binding-style utilities that know about Flow. Here are some examples of helpers that we have in our project (admittedly, some of these can have better names!):

Some variants of Flow's combine operators that know about Flow<Result<V, E>>

/**
 * Variant of Kotlin's [combine] that makes it easier to work with flows of [Result].
 *
 * Use this if [transform] never returns an error. If your transform might return an error, consider using [combineResultWithErr] instead.
 */
fun <T1, T2, E, R> combineResult(
    flow: Flow<Result<T1, E>>,
    flow2: Flow<Result<T2, E>>,
    transform: suspend (T1, T2) -> R
): Flow<Result<R, E>> {
    return combine(flow, flow2) { r1, r2 ->
        binding {
            val s1 = r1.bind()
            val s2 = r2.bind()
            transform(s1, s2)
        }
    }
}

/**
 * Variant of Kotlin's [combine] that makes it easier to work with flows of [Result].
 *
 * Use this if [transform] might return an error. If your transform never returns an error, consider using [combineResult] instead.
 */
fun <T1, T2, E, R> combineResultWithErr(
    flow: Flow<Result<T1, E>>,
    flow2: Flow<Result<T2, E>>,
    transform: suspend (T1, T2) -> Result<R, E>
): Flow<Result<R, E>> {
    return combine(flow, flow2) { r1, r2 ->
        binding {
            val s1 = r1.bind()
            val s2 = r2.bind()
            transform(s1, s2).bind()
        }
    }
}

Variant of andThen that can be applied to a Flow<Result<V, E>>

/**
 * Apply [transform] on each element in this [Flow] if the element is an [Ok] otherwise return the
 *  error as-is into the flow.
 */
fun <V, E, U> Flow<Result<V, E>>.andThen(transform: suspend (V) -> Result<U, E>): Flow<Result<U, E>> {
    return map {
        when (it) {
            is Ok -> transform(it.value)
            is Err -> it
        }
    }
}

Variant of flatMapLatest that makes it simpler to work with Result<V, E>

/**
 * Like [andThen] but allows the [transform] to return a `Flow<Result>`. This method applies the
 *  [flatMapLatest] operator allowing you to return a flow from your transform
 */
fun <V, E, U> Flow<Result<V, E>>.andThenFlow(transform: suspend (V) -> Flow<Result<U, E>>): Flow<Result<U, E>> {
    return flatMapLatest {
        when (it) {
            is Ok -> transform(it.value)
            is Err -> flowOf(it)
        }
    }
}

As you can see, these utilities work, but they can be improved a lot. Specifically, they can benefit from the equivalent of binding {} (perhaps something like flowBinding {})

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions