Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to pass multiple lambda functions in Kotlin to handle success/failure

Tags:

android

kotlin

New at Kotlin here and trying to learn the best way to use the higher order functions and passing lambdas. I've created this method to call an API and return an object created from a string OR return a failure if something went wrong.

    fun getDeviceStatus(onSuccess: (Device) -> Unit, onFailure: ((String) -> Unit)? = null) {

        FuelClient.get(DEVICE_URL,
                success = { responseString ->

                    val adapter = MoshiUtil.moshi.adapter(Device::class.java)

                    val deivce= adapter.fromJson(responseString)!!

                    onSuccess(device)
                },
                failure = { onFailure?.invoke(it.message!!)})


    }

I can use this function fine like so:

DeviceService.getDeviceStatus(
                { w ->
                    print("device")
                },
                { e -> print(e) })

But it bothers me a bit that I can't see the name of the functions to see what each function does. I"m wondering if there is a cleaner/better way to do this, like

DeviceService.getDeviceStatus(){
            onSuccess{print("device")}
            onFailure{print("error")}
        }

or maybe

DeviceService.getDeviceStatus()
                .onSuccess{print("device")}
                .onFailure{print("error")}

But those gives errors. Any thoughts on how to best handle the onSuccess/onFailure use case that is very common? Thx

like image 960
7200rpm Avatar asked May 23 '18 23:05

7200rpm


People also ask

How do you pass the lambda function in Kotlin?

Lambda expression syntaxParameter declarations in the full syntactic form go inside curly braces and have optional type annotations. The body goes after the -> . If the inferred return type of the lambda is not Unit , the last (or possibly single) expression inside the lambda body is treated as the return value.

What is the benefit of lambda expressions in Kotlin?

A lambda expression is a shorter way of describing a function. It doesn't need a name or a return statement. You can store lambda expressions in a variable and execute them as regular functions. They can also be passed as parameters to other functions or be the return value.

What is trailing lambda in Kotlin?

To help deal with this, Kotlin supports a specific kind of syntax referred to as trailing lambda syntax. This syntax states that if the final parameter to a function is another function, then the lambda can be passed outside of the function call parentheses.

Are lambda functions slower?

Lambda functions are inline functions and thus execute comparatively faster.


2 Answers

You can attach a name to each variable in kotlin. Change your code like this

DeviceService.getDeviceStatus(
                onSuccess = { w ->
                    print("device")
                },
                onFailure = { e -> print(e) })
like image 89
Suhaib Roomy Avatar answered Sep 20 '22 13:09

Suhaib Roomy


For this specific case, when the second lambda is optional, infix functions work very well:

sealed class DeviceStatusResult {
    abstract infix fun onFailure(handler: (String) -> Unit)
}

class DeviceStatusSuccess(val device: Device) : DeviceStatusResult() {
    override fun onFailure(handler: (String) -> Unit) = Unit
}

class DeviceStatusFailure(val errorMessage: String) : DeviceStatusResult() {
    override fun onFailure(handler: (String) -> Unit) = handler(errorMessage)
}

fun getDeviceStatus(onSuccess: (Device) -> Unit): DeviceStatusResult {
    // get device status
    // if (success)
    val device = Device()
    onSuccess(device)
    return DeviceStatusSuccess(device)
    // else
    // return DeviceStatusFailure(message)
}

Then it can used like

getDeviceStatus { device ->
    println(device)
} onFailure { errorMessage ->
    System.err.println(errorMessage)
}

Maybe onFailure should be called orFail or something like that.

It is good when the second argument is optional, but not so much otherwise because it doesn't force the user to actually supply a failure handler. And I don't think it's a good idea because it will be too easy to accidentally omit a failure handler. It's much better to force the user to provide one, even if it happens to be an empty one. Therefore, it is better to use named arguments for this case, even though nothing forces to actually name them.

like image 30
Sergei Tachenov Avatar answered Sep 19 '22 13:09

Sergei Tachenov