I guess the reason for stateIn having an initialVa...
# coroutines
d
I guess the reason for stateIn having an initialValue parameter is that it's needed for regular Flows, but for my case I am always using it after combining or mapping StateFlows. It's not a big deal when the output is a primitive type but when it's something more complicated creating an initial value that I know is never going to be used isn't great. Maybe there should be a version of those functions that outputs a StateFlow instead of Flow so it doesn't need to be converted with stateIn?
s
d
I think it would be worse than using the initial value since it would have to be launched async
StateFlows always have to have a state, so I don't see any inherent reason they need to be turned into Flows and then converted back when they're combined or mapped
s
Which functions are turning StateFlows into plain Flows?
d
combine, map
was hoping someone working on coroutines for Jetbrains lurks here
s
A StateFlow has a "value" property. When operators such as map, flatMap, and especially operators such as "filter", "takeXxxx", what would the returned StateFlow's "value" be? Eg a StateFlow initialized with the value 10, that hasn't emitted any other values that are larger than 20: `val otherStateFlow`:
StateFlow = stateFlow.filter { it > 20 }
What would
otherStateFlow.value
return...?
d
idk but I'm not concerned about filter
i
All of those operators are suspending operators (e.g.,
stateFlow.map { getExpensiveDataFromNetwork(it) }
), so they don't necessarily synchronously produce a
.value
in the first place, so you do need some initial value before all those operations actually return anything
d
Ok but why not make one that doesn't have that issue? Surely it's understandable why creating an object that is instantly discarded isn't desired right?
And why should the async case necessitate a worse performance and extra boilerplate for the sync case, which I would guess is much more common
a
I think I understand what @David Breneisen is trying to say here. There are times when you need a complex StateFlow that is derived from other StateFlows. An example will be
Copy code
val user = userRepo.getUser()
  .stateIn(
    initialValue = null,
    …
  )

val activities = activitiesRepo.getActivities(coordinates)
  .stateIn(
    initialValue = listOf(CYCLING, WALKING, HIKING, .. etc),
    …
  )

val canUserHaveFun = combine(
  user, 
  activities
) { user, activities -> 
    user.preference in activities
} 
// ------ THIS IS A FLOW ATM UNLESS stateIn() is USED -----
.stateIn(
    initialValue = false, // A simple example was used but at times this can be quite complex.
    …
  )
They just want to have an option to not use
stateIn()
for
canUserHaveFun
when it is more of derived stateflow from stateflows with initial values.
s
You could use the ".value" of the source StateFlow(s) to calculate the initial value of the stateIn call...
1
Copy code
stateFlow.map(::someFunction).stateIn(... intialValue=someFunction(stateFlow.value)...)
a
Never thought of that. Thanks @streetsofboston
d
This requires an extra function for even very simple mappings
Thanks for trying to help, but this is more of a feedback for jetbrains kind of topic trying to evaluate how to improve a very common use case, and hacks don't really interest me since I can already use the existing operators with initial values
👍 1
s
Even Rx(Java) didn't have this. Eg calling map on a BehaviorSubject returns an Observable. https://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/subjects/BehaviorSubject.html I guess there must be some hard problems to overcome to make what you want in a generic, composable way.
d
Yeah I'll try to get some more context from the devs on why, Ian's response might be part of it
z
There’s been quite a bit of discussion about this on the coroutines issue tracker. https://github.com/Kotlin/kotlinx.coroutines/issues/2631
👍 1
d
Thanks for the link, that's what I was looking for. I'll review the proposed solution at the bottom