Pass functions of various parameters

I have a use-case that looks like this:

fun getState() {
        viewModelScope.launch {
            logDebugMessage("getting state")

            repo.getState(warehouseId)?.let { response ->
                processResponse(response)
            }
        }
    }

I have many similar functions where I’m invoking a repository function inside a Coroutine and then processing it. The procedure is always the same, the only difference is what repository function I use and the parameters of it.

I cannot create a function like fun launchCoroutineAndCallRepoFunction(val repoFun: () -> Response) because those repository functions have various arguments.

What are my options to reduce code repetition? Specifically the logging, the coroutine scope, the repository function call and the response processing.

Something like this?

fun getState() {
    launchCoroutineAndCallRepoFunction { getState(warehouseId) }
}

fun launchCoroutineAndCallRepoFunction(repoFun: suspend Repository.() -> Response?) {
    viewModelScope.launch {
        repo.repoFun()?.let { response ->
            processResponse(response)
        }
    }
}

For logging purposes you would probably need to provide some string to the function as well.

2 Likes

That is exactly what I was looking for.

First time seeing the dot in the context of higher-order functions. Interesting!

It is called a “receiver” and you can think of it as a parameter that is used as this inside the lambda.

I don’t see a dedicated section about them in docs, but examples are scattered on this page, so just search it for “receiver”.

Also note that it wasn’t really that receiver that solved the problem. Your original function would be also fine (just after small fixes). I’ve got an impression that you tried to use your function like this:

launchCoroutineAndCallRepoFunction(repo::getState)

And this is in fact impossible, because we need to provide parameters. But this would work:

launchCoroutineAndCallRepoFunction { repo.getState(warehouseId) }

Using a receiver here is only a small improvement, so we can skip repo. in the lambda.

How would the signature of launchCoroutineAndCallRepoFunction() look?

fun getState() {
    launchCoroutineAndCallRepoFunction { repo.getState(warehouseId) }
}

fun launchCoroutineAndCallRepoFunction(repoFun: suspend () -> Response?) { ... }

So it is almost your original attempt, but with small fixes: removed val, added suspend, changed the return type to nullable.

It should work assuming that getState() has access to repo (your original getState() had access to it).

Thanks.