Overload for collectWhileInState with Builder
sockeqwe opened this issue · comments
Currently we support collectWhileInState
with flowBuilder
as shown bellow:
fun <T> collectWhileInState(
flowBuilder: (Flow<InputState>) -> Flow<T>,
handler: InStateObserverHandler<T, InputState, S>
)
I would like to add an overload with
fun <T> collectWhileInState(
flowBuilder: (InputState) -> Flow<T>, // just InputState , not Flow<InputState> as above
handler: InStateObserverHandler<T, InputState, S>
)
because I personally have more need for this variation of flowBuilder ( (InputState) -> Flow<T>
) compare to the current one (Flow<InputState>) -> Flow<T>
.
Obviously, that causes a clash on the JVM because both methods have same signature.
Thus we would need to rename one.
I would propose the following:
- My assumption is that most people have use case for the version with
(InputState) -> Flow<T>
. Thus I would keep that one under thecollectWhileInState
name.
fun <T> collectWhileInState(
flowBuilder: (InputState) -> Flow<T>, // just InputState , not Flow<InputState> as above
handler: InStateObserverHandler<T, InputState, S>
)
- I would rename the one with
Flow<InputState>) -> Flow<T>
tocollectWhileInStateWithGenericBuilder()
:
fun <T> collectWhileInStateWithGenericBuilder(
flowBuilder: (Flow<InputState>) -> Flow<T>,
handler: InStateObserverHandler<T, InputState, S>
)
Any thoughts?
Couldn't this be a foot gun? You create a Flow based on a value in the state, but if it's in the state it means that the value can change through mutations. Even if you know that the value does not change without you leaving the state first I think it would still be safer to do { stateFlow -> stateFlow.flatMapLatest { inputState -> myFlow(inputState) } }
opposed to just { inputState -> myFlow(inputState) }
. Or would the flowBuilder
in your proposal be called whenever InputState
changes and we do the flatMapLatest
internally?
Yes, but that is in general also an issue for on<Action>
and other friends because the passed (as parameter) state can already be mutated in the meantime. Example:
data class FooState(val value : Int)
inState<FooState> {
onAction<FooAction> { stateSnapshot, action ->
delay(2000)
...
// Some properties like stateSnapshot.value could be outdated because of mutations happened in parallel
loadItem(stateSnapshot.value)
...
}
In that case we need to rely on using MutateState
properly or set up inState(condition)
properly to cancel if necessary for onAction to give some guarantees
After I've added the indentity block (#480) I will also tackle this one. The motivation for flowBuilder: (Flow<InputState>) -> Flow<T>
was to enable collecting a flow that depends on a value of the state by chaining it through a flatMapLatest
. The identity block together with this proposal achieves the same:
inState<Foo> {
untilIdentityChanged({ it.bar }) {
collectWhileInState({ createFlow(it.bar) }) { ... }
}
}
This approach fits better to the descriptive nature of building a state machine and is less dangerous than the approach of transforming a flow. While refactoring some things in preparation of these changes I've also noticed that the (Flow<InputState>) -> Flow<T>
introduces additional complexity to the library. The current builder would be deprecated.
One question would be the name since the initial renaming proposal won't work since we've reached 1.x.