/ KOTLIN, FUNCTIONAL PROGRAMMING, OPERATOR

Kotlin operators

Consider 2 types X and Y, and a function f defined as:

class X
class Y

val f = { _:X -> Y() }     (1)
1 With Kotlin 1.3, it’s possible to name an unused parameter _, to explicitly tell it’s ignored

The following snippet declares a function similar to f. Then, it executes it with a parameter of type X using the invoke() function:

fun f(x: X) = Y()

val y: Y = f.invoke(X())

The Java equivalent would be Function.apply():

interface Function<T,R> {
  R apply(T t);
}

In Kotlin, it’s also possible to call the function using an equivalent syntax:

val y: Y = f(X())

This alternative syntax is possible because invoke() is an operator. Operators are a limited group of functions that offer a specific alternative syntax.

Languages such as C and Scala allow functions to be named how one wants e.g. +, !, ::= or perhaps even 🤔. Experience from using such languages has shown that the flip side of this freedom is a boom of symbol functions, that often result in (very) hard-to-read code. On the opposite side of the spectrum, Java completely disallows special characters in method names in order to avoid that problem. While sometimes the target of jokes, long method names allow unfamiliar readers to understand what the method does. Other languages have chosen a middle path, such as Groovy:

Groovy allows you to overload the various operators so that they can be used with your own classes.

All (non-comparator) Groovy operators have a corresponding method that you can implement in your own classes.

— Groovy documentation
http://groovy-lang.org/operators.html#Operator-Overloading

Kotlin follows the same approach: there is a limited number of operator functions that offer an alternative syntax. invoke() is one of them. Here’s the list of such available alternatives:

Expression Translated to

a()

a.invoke()

a(i)

a.invoke(i)

a(i, j)

a.invoke(i, j)

a(i_1, ..., i_n)

a.invoke(i_1, ..., i_n)

This allows to write interesting constructs:

data class Point(var x: Int, var y: Int)

class Translate(val x: Int, val y: Int) {
  operator fun invoke(point: Point) = Point(point.x + x, point.y + y)    (1)
}

val point = Point(1, 1)
val translate = Translate(5, 10)                                         (2)
val translated = translate(point)                                        (3)
1 Declare a new operator function - invoke()
2 Create a new instance of Translate
3 This is equivalent to translate.invoke(point), though it looks like a top-level function call

Operators can go a long way toward making one’s code be more readable. Or it can definitely be an exercise in showing-off, and achieve just the opposite. With great powers come great responsibility!

Nicolas Fränkel

Nicolas Fränkel

Nicolas Fränkel is a technologist focusing on cloud-native technologies, DevOps, CI/CD pipelines, and system observability. His focus revolves around creating technical content, delivering talks, and engaging with developer communities to promote the adoption of modern software practices. With a strong background in software, he has worked extensively with the JVM, applying his expertise across various industries. In addition to his technical work, he is the author of several books and regularly shares insights through his blog and open-source contributions.

Read More
Kotlin operators
Share this