Hero image

Writing more readable code using Kotlin's infix functions

Apr 25, 2024
Kotlin

In Kotlin, the infix keyword applied to a function allows you to omit the dot and brackets i.e. a isOlderThan b instead of a.isOlderThan(b). This can help to make your code more readable by removing unnecessary clutter and making your code resemble natural language. An infix function must have exactly one parameter as there can only be one value on the right-hand side.

Below is an example vector class with an infix function called dot that calculates the dot product of two vectors.

data class Vector(val x: Double, val y: Double) {
    infix fun dot(other: Vector): Double {
        return this.x * other.x + this.y * other.y
    }
}

What the hell is a dot product?

Well, the dot product of two vectors is the sum of the products of each component, it’s handy for calculating the angle between vectors. a.b = |a||b|cos(θ) where θ is the angle between a and b, but that’s enough math for this article.

With the dot infix function defined, we can calculate the dot product of two vectors like so vector1 dot vector2:

fun main() {
    val vector1 = Vector(3.0, 4.0)
    val vector2 = Vector(1.5, 2.5)

    val dotProduct = vector1 dot vector2

    println("$vector1 dot $vector2 is $dotProduct")
}

Infix Extension Functions

Extension functions can also be infix functions allowing you to extend the functionality of existing classes or types. For example, if you had a Person class which has a name and date of birth you might frequently want to check who is the oldest of two people, adding an infix extension function isOlderThan to the Person would be a good way to achieve this.

data class Person(val name: String, val dateOfBirth: LocalDate)

infix fun Person.isOlderThan(other: Person): Boolean {
    return this.dateOfBirth < other.dateOfBirth
}

This makes the intention of the if statement below very clear.

fun main() {
    val barry = Person("Barry", LocalDate.of(2000, 1, 1))
    val sally = Person("Sally", LocalDate.of(2000, 1, 2))

    if (barry isOlderThan sally) {
        println("${barry.name} is older than ${sally.name}")
    }
}

Real-World Examples in Kotest and Standard Kotlin

You’ll frequently come across infix functions in the standard library and many commonly used libraries.

Kotest assertions library

The Kotest assertions library makes use of infix extensions to provide the shouldBe, shouldHaveSize, etc assertions which help you write readable tests.

val magnitude = Vector(3, 4).magnitude()

magnitude shouldBe 5

Kotlin Standard Library

The standard library contains the to function which is an infix function for creating Pairs mostly for creating maps. It’s defined like so:

public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

There are many other examples such as until, matches, etc.