Chain kotlin flows depends on Result state - kotlin

I'm looking for the most "clean" way to implement the following logic:
I have N methods, everyone returns Flow<Result<SOME_TYPE>> (type are different)
I want to chain these methods, so if 1 returns Result.Success, then call 2nd and so on.
The most obvious way to do it is:
methodA().map { methodAResult ->
when (methodAResult) {
is Result.Success -> {
methodB(methodAResult).map { methodBResult ->
when (methodBResult) {
is Result.Success -> {
methodC(methodAResult).map { methodCResult ->
when (methodCResult) {
is Result.Success -> TODO()
is Result.Failure -> TODO()
}
}
}
is Result.Failure -> TODO()
}
}
}
is Result.Failure -> TODO()
}
}
But it looks like a well-known "callback hell". Do u have any ideas how to avoid it?

I believe this could be flattened with transform operator:
methodA().transform { methodAResult ->
when (methodAResult) {
is Success -> methodB(methodAResult).collect { emit(it) }
is Failure -> TODO()
}
}.transform { methodBResult ->
when (methodBResult) {
is Success -> methodC(methodBResult).collect { emit(it) }
is Failure -> TODO()
}
}.transform { methodCResult ->
when (methodCResult) {
is Success -> TODO()
is Failure -> TODO()
}
}

A slight modification to the solution provided by Михаил Нафталь
methodA()
.flatMapMerge {
when (it) {
is Result.Success -> methodB(it)
is Result.Failure -> emptyFlow()
}
}.flatMapMerge {
when (it) {
is Result.Success -> methodC(it)
is Result.Failure -> emptyFlow()
}
}.collect {
when (it) {
is Result.Success -> TODO()
is Result.Failure -> TODO()
}
}
Merging the output of one flow to another flow is the goal of flatMap so using flatMap seems a little cleaner.
If this Result class has a map, fold, or getOrNull type method this could be cleaned up a bit more and the when blocks could be removed.
Also if you need to propagate the failure to collect then you could replace the calls to emptyFlow with a flow that just outputs the failure that you want.

Badly a flatMap method still doesn't exist.
But you can use mapCatching :
methodA
.mapCatching { a -> methodB(a).getOrThrow() }
.mapCatching { b -> methodC(b).getOrThrow() }
Or make your own flatMap extension function :
fun <T, R> Result<T>.flatMap(block: (T) -> (Result<R>)): Result<R> {
return this.mapCatching {
block(it).getOrThrow()
}
}
methodA
.flatMap { a -> methodB(a) }
.flatMap { b -> methodC(b) }

I believe that in this use case you should probably use suspend functions and compose them using await().
Errors should be passed through exceptions as described here.

Related

Kotlin coroutines, how to async alist of calls and return the result as a map

var responseMap = mutableMapOf<VendorType, ChargeResponse>()
requests.forEach {
val response = when (it.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> {
throw NotImplementedError("${it.vendorType} does not support yet")
}
}
responseMap[it.vendorType] = response
}
responseMap
So I want all the service.charge function run in separate thread. Return the map when all is done
Hope to solve your problem:
Assume your service and request like this:
interface Service {
suspend fun chargeForType1(): ChargeResponse
suspend fun chargeForType2(): ChargeResponse
}
data class Request(val vendorType: VendorType)
suspend fun requestAll(requests: List<Request>): Map<VendorType, ChargeResponse> {
return coroutineScope {
requests
.map { request ->
async {
request.vendorType to when (request.vendorType) {
VendorType.Type1 -> service.chargeForType1()
VendorType.Type2 -> service.chargeForType2()
else -> throw NotImplementedError("${request.vendorType} does not support yet")
}
}
}
.awaitAll()
.toMap()
}
}

Idiomatic Arrow

I have the following method:
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain).map { it.toEntity() }.fold({ Either.left(it) }) { company ->
with (companyRepository) {
isCompanyExists(company).fold({ Either.left(it) }) { isExists ->
if (isExists) return#with Either.left(CompanyNameExists(companyDomain))
createCompany(company).fold({ Either.right(companyDomain) }) { Either.left(it) }
}
}
}
Is there a better/more idiomatic way to write this using Arrow?
It is hard to refactor because I can only assume what used methods should return. But I guess the methods returns MaybeError. In this case we can omit fold({ Either.left(it) }) and we can use map or flatMap.
internal typealias MaybeError<T> = Either<GenericError, T>
override fun createCompany(companyDomain: CompanyDomain): MaybeError<CompanyDomain> =
checkCompany(companyDomain)
.map { it.toEntity() }
.flatMap { company ->
companyRepository.isCompanyExists(company)
.flatMap { isExists ->
if (isExists) {
MaybeError.left(CompanyNameExists(companyDomain))
} else {
companyRepository.createCompany(company)
}
}
}

Get characteristic gatt from Notification

I'm using RxAndroidBle with kotlin.
So far I could set multiple notifications and i'm receiving them.
How can i map the notification to it's specific characteristic?
So far:
enter code here connectionObserver
.flatMap { rxBleConnection ->
rxBleConnection.discoverServices().flatMap { services -> services.getService(ServiceUUID).map(BluetoothGattService::getCharacteristics) }
.flatMapObservable { s -> fromIterable(s) }
.flatMap { characteristic -> rxBleConnection.setupNotification(characteristic)
.flatMap{ notificationObservable -> notificationObservable}}
}.observeOn(AndroidSchedulers.mainThread())
.subscribe{notification->notification}
Thank You
for those strugling with kotlin and RxAndroidBluetooth, after some work I make it to work, so the original code looks like
enter code here connectionObserver
.flatMap { rxBleConnection ->
rxBleConnection.discoverServices().flatMap { services -> services.getService(ServiceUUID).map(BluetoothGattService::getCharacteristics) }
.flatMapObservable { s -> fromIterable(s) }
.flatMap { characteristic ->
rxBleConnection.setupNotification(characteristic)
.flatMap { notificationObservable -> notificationObservable.flatMap { result -> Observable.just(result).zipWith(fromArray(getBytesFromUUID(characteristic.uuid))) } }
}
}.observeOn(AndroidSchedulers.mainThread())
.subscribe { notification -> onNotificationReceived(notification)
}

rx kotlin subscription not working, not receiving items

I created a function which returns an Observable<String> with file names, but I don't get any event in my subscription where I call this method. Also there is no call of onError, or onComplete
See my code:
fun getAllFiles(): Observable<String> {
val allFiles = File("/Users/stephan/Projects/Playground/kotlinfiles/")
.listFiles { file -> !file.isDirectory() }
return observable { subscriber ->
allFiles.toObservable()
.map { f -> "${f.name}" }
.doOnNext { println("Found file $it") }
.subscribe { subscriber}
}
}
fun test() {
getAllFiles()
.doOnNext { println("File name$it") }
.subscribe(
{n -> println("File: $n")},
{e -> println("Damn: $e")},
{println("Completed")})
}
Though everything is being called in the getAllFiles() function, so what am I missing?
observable is for creating an Observable from scratch but you already have Observable<String> from toObservable() so you don't need it. The code below works for me:
fun getAllFiles(): Observable<String> {
val allFiles = File("/Users/stephan/Projects/Playground/kotlinfiles/")
.listFiles { file -> !file.isDirectory }
return allFiles.toObservable()
.map { f -> "${f.name}" }
}
fun test() {
getAllFiles()
.doOnNext { println("File name $it") }
.subscribe(
{ n -> println("File: $n") },
{ e -> println("Damn: $e") },
{ println("Completed") })
}
You can also fix this by changing from:
.subscribe{subscriber}
to
.subscribe(subscriber)
but this nested Observable version is confusing to me.

Idiomatic way of handling nullable or empty List in Kotlin

Say I have a variable activities of type List<Any>?. If the list is not null and not empty, I want to do something, otherwise I want to do something else. I came up with following solution:
when {
activities != null && !activities.empty -> doSomething
else -> doSomethingElse
}
Is there a more idiomatic way to do this in Kotlin?
For some simple actions you can use the safe call operator, assuming the action also respects not operating on an empty list (to handle your case of both null and empty:
myList?.forEach { ...only iterates if not null and not empty }
For other actions. you can write an extension function -- two variations depending on if you want to receive the list as this or as a parameter:
inline fun <E: Any, T: Collection<E>> T?.withNotNullNorEmpty(func: T.() -> Unit): Unit {
if (this != null && this.isNotEmpty()) {
with (this) { func() }
}
}
inline fun <E: Any, T: Collection<E>> T?.whenNotNullNorEmpty(func: (T) -> Unit): Unit {
if (this != null && this.isNotEmpty()) {
func(this)
}
}
Which you can use as:
fun foo() {
val something: List<String>? = makeListOrNot()
something.withNotNullNorEmpty {
// do anything I want, list is `this`
}
something.whenNotNullNorEmpty { myList ->
// do anything I want, list is `myList`
}
}
You can also do inverse function:
inline fun <E: Any, T: Collection<E>> T?.withNullOrEmpty(func: () -> Unit): Unit {
if (this == null || this.isEmpty()) {
func()
}
}
I would avoid chaining these because then you are replacing an if or when statement with something more wordy. And you are getting more into the realm that the alternatives I mention below provide, which is full branching for success/failure situations.
Note: these extensions were generalized to all descendants of Collections holding non null values. And work for more than just Lists.
Alternatives:
The Result library for Kotlin gives a nice way to handle your case of "do this, or that" based on response values. For Promises, you can find the same thing in the Kovenant library.
Both of these libraries give you manner for returning alternative results from a single function, and also for branching the code based on the results. They do require that you are controlling the provider of the "answer" that is acted upon.
These are good Kotlin alternatives to Optional and Maybe.
Exploring the extension Functions Further (and maybe too much)
This section is just to show that when you hit an issue like the question raised here, you can easily find many answers in Kotlin to make coding the way you want it to be. If the world isn't likeable, change the world. It isn't intended as a good or bad answer, but rather additional information.
If you like the extension functions and want to consider chaining them in an expression, I would probably change them as follows...
The withXyz flavours to return this and the whenXyz should return a new type allowing the whole collection to become some new one (maybe even unrelated to the original). Resulting in code like the following:
val BAD_PREFIX = "abc"
fun example(someList: List<String>?) {
someList?.filterNot { it.startsWith(BAD_PREFIX) }
?.sorted()
.withNotNullNorEmpty {
// do something with `this` list and return itself automatically
}
.whenNotNullNorEmpty { list ->
// do something to replace `list` with something new
listOf("x","y","z")
}
.whenNullOrEmpty {
// other code returning something new to replace the null or empty list
setOf("was","null","but","not","now")
}
}
Note: full code for this version is at the end of the post (1)
But you could also go a completely new direction with a custom "this otherwise that" mechanism:
fun foo(someList: List<String>?) {
someList.whenNullOrEmpty {
// other code
}
.otherwise { list ->
// do something with `list`
}
}
There are no limits, be creative and learn the power of extensions, try new ideas, and as you can see there are many variations to how people want to code these type of situations. The stdlib cannot support 8 variations of these type of methods without being confusing. But each development group can have extensions that match their coding style.
Note: full code for this version is at the end of the post (2)
Sample code 1: Here is the full code for the "chained" version:
inline fun <E: Any, T: Collection<E>> T?.withNotNullNorEmpty(func: T.() -> Unit): T? {
if (this != null && this.isNotEmpty()) {
with (this) { func() }
}
return this
}
inline fun <E: Any, T: Collection<E>, R: Any> T?.whenNotNullNorEmpty(func: (T) -> R?): R? {
if (this != null && this.isNotEmpty()) {
return func(this)
}
return null
}
inline fun <E: Any, T: Collection<E>> T?.withNullOrEmpty(func: () -> Unit): T? {
if (this == null || this.isEmpty()) {
func()
}
return this
}
inline fun <E: Any, T: Collection<E>, R: Any> T?.whenNullOrEmpty(func: () -> R?): R? {
if (this == null || this.isEmpty()) {
return func()
}
return null
}
Sample Code 2: Here is the full code for a "this otherwise that" library (with unit test):
inline fun <E : Any, T : Collection<E>> T?.withNotNullNorEmpty(func: T.() -> Unit): Otherwise {
return if (this != null && this.isNotEmpty()) {
with (this) { func() }
OtherwiseIgnore
} else {
OtherwiseInvoke
}
}
inline fun <E : Any, T : Collection<E>> T?.whenNotNullNorEmpty(func: (T) -> Unit): Otherwise {
return if (this != null && this.isNotEmpty()) {
func(this)
OtherwiseIgnore
} else {
OtherwiseInvoke
}
}
inline fun <E : Any, T : Collection<E>> T?.withNullOrEmpty(func: () -> Unit): OtherwiseWithValue<T> {
return if (this == null || this.isEmpty()) {
func()
OtherwiseWithValueIgnore<T>()
} else {
OtherwiseWithValueInvoke(this)
}
}
inline fun <E : Any, T : Collection<E>> T?.whenNullOrEmpty(func: () -> Unit): OtherwiseWhenValue<T> {
return if (this == null || this.isEmpty()) {
func()
OtherwiseWhenValueIgnore<T>()
} else {
OtherwiseWhenValueInvoke(this)
}
}
interface Otherwise {
fun otherwise(func: () -> Unit): Unit
}
object OtherwiseInvoke : Otherwise {
override fun otherwise(func: () -> Unit): Unit {
func()
}
}
object OtherwiseIgnore : Otherwise {
override fun otherwise(func: () -> Unit): Unit {
}
}
interface OtherwiseWithValue<T> {
fun otherwise(func: T.() -> Unit): Unit
}
class OtherwiseWithValueInvoke<T>(val value: T) : OtherwiseWithValue<T> {
override fun otherwise(func: T.() -> Unit): Unit {
with (value) { func() }
}
}
class OtherwiseWithValueIgnore<T> : OtherwiseWithValue<T> {
override fun otherwise(func: T.() -> Unit): Unit {
}
}
interface OtherwiseWhenValue<T> {
fun otherwise(func: (T) -> Unit): Unit
}
class OtherwiseWhenValueInvoke<T>(val value: T) : OtherwiseWhenValue<T> {
override fun otherwise(func: (T) -> Unit): Unit {
func(value)
}
}
class OtherwiseWhenValueIgnore<T> : OtherwiseWhenValue<T> {
override fun otherwise(func: (T) -> Unit): Unit {
}
}
class TestBrancher {
#Test fun testOne() {
// when NOT null or empty
emptyList<String>().whenNotNullNorEmpty { list ->
fail("should not branch here")
}.otherwise {
// sucess
}
nullList<String>().whenNotNullNorEmpty { list ->
fail("should not branch here")
}.otherwise {
// sucess
}
listOf("a", "b").whenNotNullNorEmpty { list ->
assertEquals(listOf("a", "b"), list)
}.otherwise {
fail("should not branch here")
}
// when YES null or empty
emptyList<String>().whenNullOrEmpty {
// sucess
}.otherwise { list ->
fail("should not branch here")
}
nullList<String>().whenNullOrEmpty {
// success
}.otherwise {
fail("should not branch here")
}
listOf("a", "b").whenNullOrEmpty {
fail("should not branch here")
}.otherwise { list ->
assertEquals(listOf("a", "b"), list)
}
// with NOT null or empty
emptyList<String>().withNotNullNorEmpty {
fail("should not branch here")
}.otherwise {
// sucess
}
nullList<String>().withNotNullNorEmpty {
fail("should not branch here")
}.otherwise {
// sucess
}
listOf("a", "b").withNotNullNorEmpty {
assertEquals(listOf("a", "b"), this)
}.otherwise {
fail("should not branch here")
}
// with YES null or empty
emptyList<String>().withNullOrEmpty {
// sucess
}.otherwise {
fail("should not branch here")
}
nullList<String>().withNullOrEmpty {
// success
}.otherwise {
fail("should not branch here")
}
listOf("a", "b").withNullOrEmpty {
fail("should not branch here")
}.otherwise {
assertEquals(listOf("a", "b"), this)
}
}
fun <T : Any> nullList(): List<T>? = null
}
UPDATE:
kotlin 1.3 provide isNullOrEmpty now!
https://twitter.com/kotlin/status/1050426794682306562
try this! very clear.
var array: List<String>? = null
if (array.orEmpty().isEmpty()) {
// empty
} else {
// not empty
}
More simplest way would be,
if(activities?.isNotEmpty() == true) doSomething() else doSomethingElse()
Because of the safe call on activities?, the return value is Boolean? which can either be true, false or null. In order to use this expression in an if, we need to explicitly check if it's true
In addition to the other answers, you can also use the safe-call operator in combination with the extension method isNotEmpty(). Because of the safe call, the return value is actually Boolean? which can either be true, false or null. To use the expression in an if or when clause, you'll need to explictly check if it's true:
when {
activities?.isNotEmpty() == true -> doSomething
else -> doSomethingElse
}
Alternative syntax using the elvis operator:
when {
activities?.isNotEmpty() ?: false -> doSomething
else -> doSomethingElse
}
Consider using ?.forEach if appropriate
activities?.forEach {
doSmth(it)
}
If you want exactly the behavior you described I think your variant reads better then anything else more concise I can think of. (Yet simple if should suffice)
The actual method to use in Kotlin 1.3 is isNullOrEmpty like what was mentioned in this answer: https://stackoverflow.com/a/48056456/2735286
Here is an example of its usage:
fun main(args: Array<String>) {
var array: MutableList<String>? = null
println(array.isNullOrEmpty()) // true
array = mutableListOf()
println(array.isNullOrEmpty()) // true
array = mutableListOf("a")
println(array.isNullOrEmpty()) // false
}
This example prints out:
true
true
false
Kotlin 1.3 has extension isNullOrEmpty. The brief answer is:
if (activities.isNullOrEmpty) doSomething
else doSomethingElse
Extension is defined as:
fun <T> Collection<T>?.isNullOrEmpty(): Boolean
Similar extension exists for String and Array.
In my case prices is a optional.I handle the case in the following way with orEmpty() which returns the given array or an empty array if the given array is null.
val safeArray = poi.prices.orEmpty()
if (!safeArray.isEmpty()) {
...
}
Firstly I wanted to advise to make extension function in addition to #mlatu's answer, which handles else condition
public inline fun Map.forEachElse(operation: (Map.Entry) -> Unit, elseBlock: () -> Unit): Unit {
if (!empty)
for (element in this) operation(element)
else
elseBlock()
}
But the usage is no so beautiful.
Actually you are looking for a Maybe monad