Writing more readable code using Kotlin's infix functions
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 Pair
s 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.