Which Flow method do you happen to use more than t...
# coroutines
o
Which Flow method do you happen to use more than the other? 1️⃣
flatMapConcat { }
2️⃣
flatMapMerge { }
3️⃣ i dont remember using any of them
3️⃣ 13
1️⃣ 3
2️⃣ 6
f
Flatmaplatest
☝️ 8
e
depends on the use case. if it's a stream of events, maybe skipping values is bad. if it's a stream of the latest updated state, maybe skipping values is fine
3
f
I don't think there is a maplatest is it?
o
@florent there is
@ephemient even with state, I don't want to use mapLatest because you want to show the user the intermediary states (like State.Loading)
e
if there's a non-loading state that arrived while your UI thread was busy, why wouldn't you just jump directly to showing that
also flatMapConcat() == flatMapMerge(concurrency = 1)
again depends on the use case - but I've found many uses for flatMapLatest, few uses for flatMapMerge, and never had any use for flatMapConcat
o
@ephemient because I first have to compute the arrived non-loading state heavily (say something that takes 10 seconds), with mapLatest, the user wouldn't get any state (not even State.Loading)
e
Copy code
input.flatMapLatest { input ->
    flow {
        emit(State.Loading)
        val result = heavyComputation(input)
        emit(result)
    }
}
that will always show loading and cancel the previous computation whenever upstream input emits a new value
o
Or let's say, your upstream flow emits your current position (coordinates) every 5 seconds. You want to map this position flow to something that takes the coordinates and maps to a world location. This operation to convert coordinates to world location takes 10 seconds. Now correct me if I'm wrong, but if you do this with mapLatest, the final flow would NEVER emit something
@ephemient
Copy code
flow {
    emit("x=1,y=2")
    delay(5000)
    emit("x=1,y=2")
    delay(5000)
    emit("x=1,y=3")
    delay(5000)
}.mapLatest { coordinates ->
    val worldLocationName = compute(coordinates) // takes 10 seconds
    worldLocationName 
}
This flow never emits, if im not mistaken
e
so don't use mapLatest
o
but in this case, the flow would still represent State, right?
they aren't events
e
I never said that should imply mapLatest, just possibly flatMapLatest
of course if your producer is faster than your consumer then mapLatest doesn't complete
o
Doesnt the same point apply to flatMapLatest? Except it's for returning flows
e
not if you emit the loading state immediately
o
I meant the above code example, but then the for flatMapLatest
e
looking around... I don't have any usages of mapLatest in the client. I can see some places where we might use it in our backend (but it's not using coroutines yet so whatever)
I gave an example of how I would use it though. it's exactly the right tool for that job
o
Because don't you want to rather show old data to the user than nothing most of the time?
e
if it's as a result of a user action, no
o
Could you give a example?
Of the usecase?
e
user presses a button which shows them a new post which has to be fetched from the server. continuing to show the old post is just confusing.
o
Hmm, in that case, showing nothing would be indeed better than showing the old post
What about a chat conversation list?
Imagine a repository with alot of raw chat data, and another repository with user profiles, using them together to construct conversation previews
Now imagine a situation where there are 10 groupchats and everyone is spamming in the chat
Following it from the conversation list, there is a high chance the UI would be out of sync with mapLatest/flatmaplatest, because the consumer (the constant changing new raw conversation state), is constantly being recomputed to show a conversation preview, thus the user probably only finally seeing the conversation list when the spam fest is over with
e
having a single state object for that is going to be messy regardless
ideally you could show partial computations along the way, although how to do that may be tricky
o
Ok, let's say each conversation has his own flow
Still, if in that one conversation alot of people are spamming the chat, you would still miss some message info with mapLatest
Only after the spamfest is over
e
if you're looking at a chat overview it seems reasonable to skip updates you can't keep up with, as long as you don't show an inconsistent state
Copy code
latestMessageInEachConversation = conversationsFlow.flatMapLatest { conversations -> conversations.map { conversation -> conversation.map { it.latestMessage() }.onSubscription { emit(LOADING) } }.combineLatest { ... } }
or something along those lines
o
Do you think it's reasonable to have a chat overview that doesn't change (talking about last message of conversations) for 10 seconds because a spamfest happened for 10 seconds? I think between those 10 seconds, the user would rather see the messages that were being spammed in the middle of the conversation, than still seeing the very old messages
e
^ it will show any updates that the collector is capable of collecting
l
flatMapLatest
is by far the most useful variant as it works with infinite cancellable flows, which are very common, compared to finite Flows you'd need for
flatMapConcat
to be any useful.
☝️ 3
Try writing it, maybe you'll find out 😉
o
.flattenLatest()
Unresolvered reference: flattenLatest
Found out it doesn't exist
l
You said that twice now.
I meant writing the code so it exists for you, not writing the name of something that is not implemented yet.
o
Probably would take some effort first to understand the operators, looking at .flattenMerge()'s implementation, I don't know really either what it does
But I think you are trying to say that .flattenLatest() would be impossible?
l
I'm not saying that
I'm only telling you to give it a try. With
transformLatest
, you can write it quite easily I think
☝️ 1
o
I'm just trying to understand why it hasn't been implemented while flattenConcat() and flattenMerge() have been
l
It's most likely priorities or ambiguity leading to tough API design questions. You'll answer your own question if you try writing that function.
Also, if you succeed, you might send a PR to kotlinx.coroutines, or open an issue with the snippet
Might also be lack of use case, or that there's a better way to do what you want to do. In Kotlin, and in kotlinx.coroutines, there's no API with no use case.