groostav
07/19/2023, 4:43 PM@ObsoleteAPI
Is there an article I can read that will sell me on
when you used channels you hated this, but now, if you use flows, you can change this bad thing into this much better thing!? One reason is that I was using channels as a kind of value objects. Some component would parse a header message and pass the channel off to another function to read out the reamaining messages according to some protocol implied by the first message. This was with GRPC streams. Was it a bad idea? whats a better pattern?
Sam
07/19/2023, 4:54 PMkevin.cianfarini
07/19/2023, 5:00 PMFrancesc
07/19/2023, 5:03 PMkevin.cianfarini
07/19/2023, 5:12 PMsealed interface GRPCMessage {
enum class Header : GRPCMessage { TypeA, TypeB }
data class Content(…) : GRPCMessage
}
flow<GRPCMessage> { … }
.withHeaderType()
.collect { (header, content) ->
when (header) {
TypeA -> processTypeAContent(content)
TypeB -> processTypeBContent(content)
}
}
/**
* Strips the first element from this stream and passes it to every subsequent content message in the stream.
*This function assumes that the receiver flow's first element will be a header.
*/
fun Flow<GRPCMessage>.withHeaderType(): Flow<Pair<Header, Content>> {
return flow {
var header: Header? = null
collect { message ->
if (header == null) {
header = message as Header
} else {
emit(Pair(header!!, message as Content))
}
}
}
}
kevin.cianfarini
07/19/2023, 5:13 PMgroostav
07/19/2023, 8:21 PMselect
which is still very difficult with flows. If there's a combinator that does what you want that combines multiple flows your set. If there isn't, you have to write some really strange looking code.groostav
07/19/2023, 8:32 PMChannel
as a kind of reaminingMessages: Channel<Message>
, and then I had functions that took those messages, sometimes as actors sometimes as producers sometimes as one-shots. I have a background in parsers so this was ind've a logical step for me. But Flow
s are, a little trickier.
As I read once from jetbrains, Channel
seems very unopinionated, whereas Flow
seems very opinionated. Im not saying they're bad opinions, they're just not the ones I had organicallykevin.cianfarini
07/19/2023, 8:55 PMchannelFlow
) but flows are more flexible than channels in many cases. I can write a flow that produces elements without any underlying callback or queue.
flow {
var i = 0
while (true) {
emit(i++)
delay(10)
}
}
To do the same with a channel you’d need to launch some sort of async task that runs concurrently to the function that returns you a channel:
fun CoroutineScope.produceNumbers(): Channel<Int> {
val channel = Channel(...)
launch {
var i = 0
while (true) {
channel.send(i++)
delay(10)
}
}
return channel
}
This is one of the numerous benefits of having a cold API as well as a hot API.kevin.cianfarini
07/19/2023, 8:56 PMfranztesca
07/19/2023, 10:48 PMgroostav
07/21/2023, 3:36 PMproduce
with a Rendezvous
channel possibly with Unconfined
dispatch is going to get you very close to your flow, but it will be hot (producer runs first, element is cached) rather than cold (producer called on demand)groostav
07/21/2023, 3:39 PMfun CoroutineScope.produceNumbers(): Chan<Int> {
return produce<Int>(Channel.Rendezvous) {
var i = 0
while(true){
channel.send(i++)
delay(10)
// you will not get an int every 10ms
}
}
}
groostav
07/21/2023, 3:43 PMcold flow instead is a more abstract data producer whose lifecycle is limited by collector duration. For this reason a cold flow is inherently structured and encapsulated, while a channel is not (producer is not bound to the consumer, and it is an external actor).With respect to life cycle management this necessarily makes all
Channel
instances Closeable
and thus you have to clean them up. I'd like to say this isn't that difficult, but in practice it has been. Right now, have some rather tricky cleanup bugs that I'm chasing down, related to a Channel is closed
making some tests into failures --of course, production doesn't care because this only happens at shutdown time.
We rarely use channel, as most often the corresponding things can be expressed as a cold Flow.And this is exactly my point, I've spent some time with flows and Im maybe a C+ person at properly using the flow combinators, but I find myself butting up against flow limitations frequently. I suspect you and your team would deftly navigate these limitations by understanding something about flows or thinking about flows in a way I do not, but I struggle, and frequently think "well if I just had a channel here and a channel there then this feature becomes a simple
select{}
call."groostav
07/21/2023, 3:47 PMFlow
is like a Collection
where the only exposed method of consumption is foreach
(ie consume
), where a channel is more like an Iterator
which lets you consume elements from it piece meal, and that's just how I've traditionally written code.
That said, if you think about how often one uses things like indexing operators into lists or headSet
on a NavigableSet
(did you even know that was an interface), its just vastly more common to use foreach
, especially when you have combinators as powerful as filter
and flatMap
.
I'm clearly on the wrong team here, its just, I guess the only way to get on-side is to force myself to use flows 🙃kevin.cianfarini
07/21/2023, 4:16 PMi think aThis still requires a coroutine queueing elements up into the channel and a separate coroutine consuming those elements. That’s the main distinction I was trying to illustrate.with aproduce
channel possibly withRendezvous
dispatch is going to get you very close to your flow, but it will be hot (producer runs first, element is cached) rather than cold (producer called on demand)Unconfined
Sam
08/15/2023, 3:40 PMgroostav
08/15/2023, 3:41 PMSam
08/15/2023, 3:43 PMSam
08/15/2023, 3:43 PMSam
08/15/2023, 3:47 PMkevin.cianfarini
08/15/2023, 4:37 PMSam
08/15/2023, 4:37 PMPablichjenkov
08/15/2023, 5:20 PMSam
08/15/2023, 5:23 PMxoangon
08/15/2023, 5:30 PMSharedFlow
section.
Besides from that, do you have any real world example where channels are a good fit? I’m having it tough to picture by myselfSam
08/15/2023, 5:41 PMSam
08/20/2023, 3:05 PMxoangon
08/20/2023, 6:08 PM