https://kotlinlang.org logo
Title
o

Orhan Tozan

03/01/2021, 6:16 PM
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
Wow majority of people never seem to flatMap😅 I wonder how you guys do it when you need to bring a Flow into a map
f

florent

03/01/2021, 6:42 PM
Flatmaplatest
☝️ 8
o

Orhan Tozan

03/01/2021, 6:44 PM
Interesting, if you like to use flatMapLatest, do you also prefer mapLatest over map?
e

ephemient

03/01/2021, 6:57 PM
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

florent

03/01/2021, 6:57 PM
I don't think there is a maplatest is it?
o

Orhan Tozan

03/01/2021, 6:58 PM
@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

ephemient

03/01/2021, 7:00 PM
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

Orhan Tozan

03/01/2021, 7:03 PM
@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

ephemient

03/01/2021, 7:06 PM
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

Orhan Tozan

03/01/2021, 7:08 PM
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
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

ephemient

03/01/2021, 7:11 PM
so don't use mapLatest
o

Orhan Tozan

03/01/2021, 7:12 PM
but in this case, the flow would still represent State, right?
they aren't events
e

ephemient

03/01/2021, 7:12 PM
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

Orhan Tozan

03/01/2021, 7:13 PM
Doesnt the same point apply to flatMapLatest? Except it's for returning flows
e

ephemient

03/01/2021, 7:13 PM
not if you emit the loading state immediately
o

Orhan Tozan

03/01/2021, 7:14 PM
I meant the above code example, but then the for flatMapLatest
e

ephemient

03/01/2021, 7:14 PM
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

Orhan Tozan

03/01/2021, 7:15 PM
Because don't you want to rather show old data to the user than nothing most of the time?
e

ephemient

03/01/2021, 7:17 PM
if it's as a result of a user action, no
o

Orhan Tozan

03/01/2021, 7:18 PM
Could you give a example?
Of the usecase?
e

ephemient

03/01/2021, 7:24 PM
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

Orhan Tozan

03/01/2021, 7:25 PM
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

ephemient

03/01/2021, 7:30 PM
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

Orhan Tozan

03/01/2021, 7:31 PM
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

ephemient

03/01/2021, 7:33 PM
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
latestMessageInEachConversation = conversationsFlow.flatMapLatest { conversations -> conversations.map { conversation -> conversation.map { it.latestMessage() }.onSubscription { emit(LOADING) } }.combineLatest { ... } }
or something along those lines
o

Orhan Tozan

03/01/2021, 7:37 PM
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

ephemient

03/01/2021, 7:38 PM
^ it will show any updates that the collector is capable of collecting
l

louiscad

03/01/2021, 10:26 PM
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
o

Orhan Tozan

03/03/2021, 9:27 AM
Why is there not a Flow<Flow<T>>.flattenLatest()? (flattenConcat() and flattenMerge() exist). For example, sometimes my
combine
wants to return a Flow (so you get a Flow in a Flow). I can't flattenLatest this unfortunately
l

louiscad

03/03/2021, 11:14 AM
Try writing it, maybe you'll find out 😉
o

Orhan Tozan

03/03/2021, 11:17 AM
.flattenLatest()
Unresolvered reference: flattenLatest
Found out it doesn't exist
l

louiscad

03/03/2021, 11:19 AM
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

Orhan Tozan

03/03/2021, 11:22 AM
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

louiscad

03/03/2021, 11:22 AM
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

Orhan Tozan

03/04/2021, 12:42 PM
I'm just trying to understand why it hasn't been implemented while flattenConcat() and flattenMerge() have been
l

louiscad

03/04/2021, 2:50 PM
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.