The Power of Composition: Building Big from Small
It’s not the pieces themselves that make something great — it’s how you put them together.” — Anonymous (and every LEGO master ever)
If there’s one concept that captures the heart of Functional Programming, it’s composition. Think of it like connecting small, reusable building blocks to form something powerful and beautiful. Whether you’re combining simple functions to form complex logic, or merging reusable UI components, composition is the art of building big things from small, pure parts.
Why Composition Matters
In an imperative world, we often think step-by-step:
val numbers = listOf(1, 2, 3)
var doubled = mutableListOf<Int>()
for (n in numbers) {
doubled.add(n * 2)
}
val result = doubled.filter { it > 3 }You’re telling how to do it — manually instructing every move.
In a functional world, we describe what to do:
val result = listOf(1, 2, 3)
.map { it * 2 }
.filter { it > 3 }You’re composing transformations, like chaining mini-machines that work together.
Real-Life Analogy: The Coffee Bar
Imagine a coffee bar.
The barista doesn’t reinvent the recipe every time.Instead, they compose a drink by combining small steps — grinding beans, brewing coffee, adding milk. Each step is reusable, predictable, and can be rearranged to make different drinks.
That’s function composition — combine existing, reliable parts instead of writing new code from scratch.
Composition in Kotlin
fun f(x: Int) = x + 1
fun g(x: Int) = x * 2
// Compose manually
val result = f(g(3)) // (3 * 2) + 1 = 7Or create a reusable compose helper:
fun <A, B, C> compose(f: (B) -> C, g: (A) -> B): (A) -> C = { x -> f(g(x)) }
val composed = compose(::f, ::g)
println(composed(3)) // 7Composition — Building Big from Small
The Laws of Composition
Composition isn’t just about connecting things — it also follows powerful mathematical laws that make reasoning about code much easier.
Let’s meet our two heroes: Associative and Identity.
Associative Law — Order of Grouping Doesn’t Matter
“Whether you stack bricks from the left or the right, the wall still stands strong.”
In Kotlin:
val f: (Int) -> Int = { it + 1 }
val g: (Int) -> Int = { it * 2 }
val h: (Int) -> Int = { it - 3 }
val left = f(g(h(5))) // f ∘ (g ∘ h)
val right = f(g(h(5))) // (f ∘ g) ∘ h
println(left == right) // true — associativeThis means composition is predictable and reorder-safe.
Just like applying filters in a photo editing app — whether you group brightness before contrast or after, the pipeline behaves consistently.
Identity Law — Doing Nothing Still Works!
“Sometimes doing nothing is the best thing you can do.” — Lao Tzu (and every functional programmer ever)
In code, the identity function is simply:
val id: (String) -> String = { it }
val f: (String) -> String = { it.uppercase() }
println(f(id("hello"))) // HELLO
println(id(f("hello"))) // HELLOWhether you apply id before or after f, the result is the same.
It’s like looking into a mirror — you see exactly what’s in front of it. No change, no distortion — just reflection.
The Core Idea
Composition gives your code clarity, reuse, and elegance.
You build functions like Lego blocks — modular, replaceable, and easy to test.
And because of laws like Associativity and Identity, you can reason about your code with confidence — just like solving math equations.
“Good design is not when there’s nothing more to add, but when there’s nothing left to take away.” — Antoine de Saint-Exupéry
Now that we’ve seen how composition lets us build big ideas from small pieces, it’s time to explore the real heroes behind it — Higher-Order Functions, where functions themselves become the building blocks of logic.
