Ryan Simon
07/09/2020, 8:47 PMFlow<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
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.octylFractal
07/09/2020, 8:50 PMtransform
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 foroctylFractal
07/09/2020, 8:51 PMRyan Simon
07/09/2020, 8:54 PMmap
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
octylFractal
07/09/2020, 8:57 PMoctylFractal
07/09/2020, 8:58 PMFlow.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 ListoctylFractal
07/09/2020, 8:59 PMRyan Simon
07/09/2020, 9:03 PMRyan Simon
07/09/2020, 9:13 PMoverride 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:
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;:
Ryan Simon
07/09/2020, 9:13 PMoctylFractal
07/09/2020, 9:15 PMoctylFractal
07/09/2020, 9:15 PM.map
to a separate functionRyan Simon
07/09/2020, 9:16 PMRyan Simon
07/09/2020, 9:44 PM