Phase 5 · Advanced KotlinModule 29~34 min read

Functional Kotlin

Embrace immutability, function composition, and functional error handling with Arrow.

What you'll learn

Kotlin isn't a purely functional language, but it embraces functional ideas that make code safer and clearer — immutability, pure functions, and composition. This module ties them together and points to the Arrow library for going further.

By the end you'll be able to:

  • Write pure functions and prefer immutable data
  • Compose functions
  • Use Pair and Triple
  • Know what the Arrow library offers

Immutability & purity

A pure function returns the same output for the same input and has no side effects (it doesn't change anything outside itself). Combined with immutable data — Kotlin's val, read-only collections, and data class + copy() — pure functions make code dramatically easier to reason about and test:

Pure.kt
// a PURE function: same input -> same output, no side effects
fun add(a: Int, b: Int) = a + b

// prefer immutable data + transformations over mutation
val original = listOf(1, 2, 3)
val doubled = original.map { it * 2 }   // a NEW list; original untouched

println(original)   // [1, 2, 3]
println(doubled)    // [2, 4, 6]

Key idea

The functional mindset: don't mutate data, transform it. Instead of changing a list in place, produce a new one with map/filter. Instead of editing an object, make a copy(). Immutable-plus-pure is the safest foundation for concurrent code (Phase 6).

Function composition

Because functions are values (Module 8), you can compose them — build complex behaviour by combining simple functions. Small, pure functions that compose are the essence of functional style:

Compose.kt
val addOne = { x: Int -> x + 1 }
val double = { x: Int -> x * 2 }

// compose two functions into a new one
val addThenDouble = { x: Int -> double(addOne(x)) }

println(addThenDouble(5))   // (5 + 1) * 2 = 12

Pair & Triple

For quickly grouping two or three values without defining a class, use Pair and Triple. They're destructurable (Module 25) and great for returning multiple values:

Tuples.kt
val pair = "Sara" to 25          // a Pair<String, Int>
val (name, age) = pair
println("$name, $age")

val point = Triple(1, 2, 3)
println(point.first + point.second + point.third)   // 6

Note

Use Pair/Triple for quick, local groupings. For anything that lives beyond a function or has meaning, prefer a named data class — Person(name, age) is far clearer than an anonymous Pair<String, Int>.

The Arrow library

For serious functional programming, the Arrow library adds types Kotlin's standard library doesn't: Either<Error, Value> for typed error handling without exceptions, Option<T> for a richer "value or nothing," and tools for composition and validation:

Arrow.kt
// The Arrow library adds functional types like Either<Error, Value>
// for typed error handling without exceptions:

fun divide(a: Int, b: Int): Either<String, Int> =
    if (b == 0) "Cannot divide by zero".left()
    else (a / b).right()

// Option<T> models "a value or nothing" more richly than a plain null.

Note

Arrow is optional and adds a learning curve — most Kotlin projects get by with the standard library's Result, sealed classes, and null safety. But if you love functional programming, Arrow brings the full toolkit to Kotlin.

Recap & quick check

Key takeaways

  • A pure function has no side effects and returns the same output for the same input.
  • Prefer immutable data and transformation (map/copy) over mutation — the safest foundation, especially for concurrency.
  • Functions are values, so you can compose them from small, pure pieces.
  • Pair and Triple group a few values quickly; prefer a named data class for anything meaningful.
  • The Arrow library adds Either, Option, and more for advanced functional programming (optional).

Quick check

1. What makes a function 'pure'?

2. What's the functional way to 'change' data?

3. Why can you compose functions in Kotlin?

4. When should you prefer a data class over a Pair?

5. What does the Arrow library add?

Excellent — you've completed Phase 5! You command Kotlin's advanced features — operators, DSLs, error handling, and functional style. Next comes the big one: Phase 6 — Coroutines & Asynchronous Programming.