hey all, got a super weird `Flow<T>` situati...
# coroutines
r
hey all, got a super weird
Flow<T>
situation that I'd love to get some feedback on. the gist is that I'm wanting to transform some
Flow<T>
to some
Flow<R>
, where the
transform
function receives a
Result<T>
, but should spit out a
Result<R>
to line up with the return type of my function here's what the existing code looks like
Copy code
override fun getEquipment(type: String): Flow<Result<List<Equipment>>> {
        return apiCallHandler.process { equipmentApi.getEquipment(type) }
            .transform { result ->
                result.onSuccess { response ->
                    if (response.equipment.isNotEmpty()) {
                        val equipmentList = ArrayList<Equipment>()

                        for (equipment in response.equipment) {
                            equipmentList.add(equipment.toEquipment())
                        }

                        emit(Result.success(equipmentList))
                    } else {
                        emit(Result.failure(EquipmentDataFailure.NoEquipmentFound))
                    }
                }
            }
    }
The main issue here is that if the
result.onSuccess
is never hit, the Flow will not emit anything and we won't know about it
From what I can tell, this is because the
result
parameter given to us in the
transform
lambda is of type
Result<T>
, so if we just pass it through the
Flow
, it will terminate the
Flow
because we're expecting a
Flow
with a
Result<R>
(that's List<Equipment> in the example above) The way to work around this is to handle the
result.onFailure
case and
emit
from there. The thing is that there's no hint at having to do this from the IDE/compiler. Am I doing something I shouldn't be? And if not, maybe this is something that should be filed with the Kotlin team? Weird one for sure.
o
I don't know if there's anything that unexpected about this, I think the only thing I might suggest is that
transform
is a
1 -> N
mapping, where N can be zero, but
map
is a
1 -> 1
mapping, which is what I think you were going for
to me I would read this as you intended for failure to not do anything, as you explicitly handle only success
👍🏾 1
r
yeah, maybe
map
is the better operator for my use case though what I really wanted was for the
Result
to pass through if it wasn't successful my
process
function returns a
Result<T>
that may or may not be a
success
. if it's not a
success
, i want the
failure
to just pass through without being touched by the
transform
o
(1)
Flow.map
to ensure 1 -> 1 (2)
Result.mapCatching
to handle success/failure in mapping to new result (3)
equipment.map
for a simpler transformation to List
r
i'll give that a shot, thank you!
@octylFractal maybe you can help me with this new interesting nugget lol. here's my new function
Copy code
override fun getEquipment(type: String): Flow<Result<List<Equipment>>> {
        return apiCallHandler.process { equipmentApi.getEquipment(type) }
            .map { result ->
                result.mapCatching { response ->
                    if (response.equipment.isEmpty()) {
                        throw EquipmentDataFailure.NoEquipmentFound
                    } else {
                        response.equipment.map { it.toEquipment() }
                    }
                }
            }
    }
Now I get this error from the compiler:
Copy code
org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Couldn't transform method node:
emit$$forInline (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;:
i can't win 😢
o
I suspect a potential workaround is to extract the content of
.map
to a separate function
r
lol i just had to be the guy to find the bleeding edges. thanks again, Octavia
👍 1
confirmed that this was indeed related to that issue, and the latest EAP for Kotlin 1.4 fixed the problem for me.
👍 1