Skip to main content

Kotlin Infix Notation

Infix notation is one of Kotlin's powerful features that allows you to call certain functions without using the standard method call syntax. Instead of the traditional dot and parentheses, infix notation enables you to write function calls in a more natural, readable way that resembles plain English. This feature is particularly important for building expressive Domain-Specific Languages (DSLs) in Kotlin.

What is Infix Notation?

In regular function calls in Kotlin, you typically write:

kotlin
object.function(argument)

With infix notation, you can write the same call as:

kotlin
object function argument

This subtle change makes your code read more like a natural language statement rather than a programming construct.

How to Create Infix Functions

To create an infix function in Kotlin, you need to follow these requirements:

  1. Mark the function with the infix modifier
  2. The function must be a member function or an extension function
  3. The function must have exactly one parameter
  4. The parameter must not accept variable arguments (vararg)
  5. The parameter must not have a default value

Here's the basic syntax:

kotlin
infix fun ReceiverType.functionName(parameterName: ParameterType): ReturnType {
// function body
}

Basic Examples

Let's start with a simple example to understand infix notation:

kotlin
class Person(val name: String) {
infix fun likes(thing: String): String {
return "$name likes $thing"
}
}

fun main() {
val john = Person("John")

// Regular function call
println(john.likes("apples"))

// Infix notation call
println(john likes "apples")
}

Output:

John likes apples
John likes apples

Both function calls produce the same result, but the infix version reads more naturally, like a simple English sentence.

Creating Extension Functions with Infix Notation

You can also define infix notation for extension functions:

kotlin
infix fun Int.isMultipleOf(number: Int): Boolean {
return this % number == 0
}

fun main() {
// Regular way
println(10.isMultipleOf(5))

// Infix way
println(10 isMultipleOf 5)
println(10 isMultipleOf 3)
}

Output:

true
true
false

Practical Use Cases

1. Building a Simple DSL for Testing

Infix functions are perfect for creating expressive testing frameworks:

kotlin
infix fun <T> T.shouldEqual(expected: T) {
if (this == expected) {
println("Test passed! ✅")
} else {
println("Test failed! ❌ Expected $expected but got $this")
}
}

fun main() {
val result = 2 + 2
result shouldEqual 4
result shouldEqual 5
}

Output:

Test passed! ✅
Test failed! ❌ Expected 5 but got 4

2. Working with Collections

Infix notation makes collection manipulation more readable:

kotlin
infix fun <T> List<T>.containsAll(elements: List<T>): Boolean {
return this.containsAll(elements)
}

infix fun <T> MutableList<T>.addElements(elements: List<T>): MutableList<T> {
this.addAll(elements)
return this
}

fun main() {
val fruits = listOf("apple", "banana", "orange")
val searchItems = listOf("banana", "apple")

println(fruits containsAll searchItems)

val mutableList = mutableListOf(1, 2, 3)
mutableList addElements listOf(4, 5, 6)
println(mutableList)
}

Output:

true
[1, 2, 3, 4, 5, 6]

3. Building a Time DSL

Creating a mini-DSL for time calculations becomes more elegant with infix notation:

kotlin
class TimeValue(val value: Int) {
infix fun days(from: String): String {
val direction = if (from == "ago") "past" else "future"
return "Point in $direction: $value days"
}
}

infix fun Int.days(relation: String): TimeValue {
return TimeValue(this)
}

fun main() {
println(5 days "ago")
println(10 days "from now")
}

Output:

Point in past: 5 days
Point in future: 10 days

Precedence of Infix Function Calls

It's important to understand that infix function calls have lower precedence than arithmetic operators, type casts, and the rangeTo operator. They have higher precedence than boolean operators (&& and ||), is, in, and other operators.

kotlin
// This works as expected
println(1 + 2 isMultipleOf 3)

// This requires parentheses because && has lower precedence
println((1 isMultipleOf 1) && (4 isMultipleOf 2))

When in doubt, use parentheses to make the order of operations clear.

Built-in Infix Functions in Kotlin

Kotlin provides several built-in infix functions:

kotlin
fun main() {
// to creates a Pair
val pair = "key" to "value"
println(pair)

// in checks membership
println(3 in 1..10)

// until creates a range exclusive of the upper bound
println(1 until 5)

// step defines the step for a range
println((1..10 step 2).toList())

// downTo creates a descending range
println((5 downTo 1).toList())
}

Output:

(key, value)
true
1..4
[1, 3, 5, 7, 9]
[5, 4, 3, 2, 1]

Best Practices for Using Infix Notation

  1. Use for readability: Only use infix notation when it genuinely improves readability
  2. Keep it simple: Infix functions should be simple and intuitive
  3. Choose good names: Use descriptive verb or preposition-like names
  4. Be consistent: If you create an infix function, use it consistently in your code
  5. Document well: Always document what your infix functions do, especially in DSLs

Summary

Kotlin's infix notation allows you to write more expressive and readable code by enabling function calls that resemble natural language statements. This feature is particularly useful for:

  • Creating domain-specific languages (DSLs)
  • Writing more expressive test assertions
  • Making code that manages collections or domain objects more readable
  • Building fluent interfaces

The key to effective use of infix notation is understanding when it enhances readability versus when it might confuse readers who are unfamiliar with your code.

Additional Resources

Exercises

  1. Create an infix function at for a List<T> that returns the element at the specified index
  2. Build a simple calculator DSL that uses infix functions for operations like plus, minus, etc.
  3. Design a fluent API for building an HTML document using infix notation
  4. Implement an infix function power for raising an integer to a specified power
  5. Create a mini-DSL for SQL-like queries using infix notation (e.g., select name from users where id eq 1)

Try solving these exercises to strengthen your understanding of infix notation in Kotlin!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)