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 transformoctylFractal
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