Hi, my name is André from Florianópolis, Brazil. I'm a backend developer, currently working at Ifood.

Partial method reference

27/03/2021

When you start working with Kotlin, as it's a programming language that mixes paradigms, it's common to build a chain of scope functions for better code.

Disclaimer: in version 0.13 of arrow-kt, the invoke function was depreciated, and they started recommending to use Partially and curied. For more information, check this gist (arrow-kt-partial-method-reference.kt)

I love to use method reference, whoever knows me better knows that I say this every day

But... method reference has a big problem that you can only use with functions that have one parameter.

val someValue = "123"

fun saveToFirebase(list: List<Item>) = service.save(list)

list
    .filter(::checkSomeVariable)
    .also(::logItems)
    .let(::saveToFirebase)

What happens if saveToFirebase functions needs to receive more than one parameter? You will need to remove method reference and use lambda.

val someValue = "123"

fun saveToFirebase(list: List<Item>, value: String) = service.save(list, value)

list
    .filter(::checkSomeVariable)
    .also(::logItems)
    .let { items -> saveToFirebase(items, someValue) }

To fix this problem, and we stay using method reference, I recommend arrow-syntax.

Installing

It's necessary to add this dependency to your project:

implementation("io.arrow-kt:arrow-syntax:<version>")

Using

It's important to remember that IntelliJ doesn't auto-import correct these functions, therefore, it's necessary to add import arrow.syntax.function.invoke import to your classes.

The function that will be called using partial method reference needs that the last parameter received is always the one coming from the chained methods, like in our example that receives a list of items:

fun saveToFirebase(value: String, list: List<Item>) = service.save(list, value)

And to call this function, you will need to pass the values ​​in parentheses right after the method reference that invokes the method.

list
    .filter(::checkSomeVariable)
    .also(::logItems)
    .let((::saveToFirebase)(someValue))

You can use partial method reference with any quantity of parameters BUT I really don't recommend to use more than two parameters, besides your code will be hard to read.

fun saveToFirebase(value: String, anotherValue: String, list: List<Item>) = service.save(list, value)

list
    .filter(::checkSomeVariable)
    .also(::logItems)
    .let((::saveToFirebase)(someValue)(anotherValue))