skip to content

Higher-Order Functions

Functions that take or return other functions, and the composition idioms built on them. This is where interviewers check whether your functional style is deliberate or just lambda-flavored imperative code.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

15

What does it mean to compose two functions in Kotlin, and how would you write an `andThen` helper that runs one function and feeds its result into another?

level: juniorimportance: must knowfreq 55%

answer

  1. andThen = receiver first; compose = receiver last
  2. Returns a new lambda { x -> g(this(x)) }
  3. Extension on a function type, marked infix
  4. Three generics A, B, C thread the intermediate type
  5. Lazy: nothing runs until you invoke the result

basics

~10 s

Composing means chaining functions so one's output becomes the next's input. An andThen helper takes function f, then g, and returns a new function that does g(f(x)) for any input x.

solid answer

~40 s

Composition combines two function values into a single new function. In Kotlin you write it as an extension on a function type, usually `infix`. `andThen` applies the receiver first, then the argument: `fun <A,B,C> ((A)->B).andThen(g:(B)->C):(A)->C = { x -> g(this(x)) }`. The returned lambda captures both functions and defers execution until called. `compose` is the mirror image: `f.compose(g)` returns `{ x -> f(g(x)) }`, running g first. The key idea is that the helpers don't run anything immediately; they build and return a brand-new `(A)->C` lambda. You then invoke it like any function: `val pipeline = parse andThen validate andThen save; pipeline(input)`. Generic type parameters thread the intermediate type B so the compiler checks the wiring.

code

kotlin · 7 lines
kotlin
infix fun <A, B, C> ((A) -> B).andThen(g: (B) -> C): (A) -> C =
    { x -> g(this(x)) }

val trim: (String) -> String = String::trim
val len: (String) -> Int = String::length
val trimmedLength = trim andThen len
println(trimmedLength("  hi  ")) // 2

go deeper

for a junior

Can state that composition chains output-to-input and write a simple andThen returning g(f(x)).

for a middle

Distinguishes andThen vs compose order, uses infix + generics, and knows it returns a lazy new lambda.

for a senior

Explains receiver-as-function, closure capture, and when a fluent pipeline beats nested calls for readability.

for a principal

Frames composition as a building block for declarative pipelines and weighs it against existing collection/sequence operators in real code.

## What composition means **Function composition** takes two functions and produces a third that, when called, runs them in sequence. If `f: (A) -> B` and `g: (B) -> C`, the composition is a function `(A) -> C` defined by `x -> g(f(x))`. The two directions have conventional names: - **`andThen`**: receiver runs **first**. `f.andThen(g)` == `{ x -> g(f(x)) }`. Reads left-to-right like a pipeline. - **`compose`**: receiver runs **last** (math convention). `g.compose(f)` == `{ x -> g(f(x)) }`. Reads right-to-left. ## Writing the helper Kotlin has no built-in `andThen` for plain function types, so you define an **extension function** on the function type. Marking it `infix` lets you write `a andThen b` without dots/parens. ```kotlin infix fun <A, B, C> ((A) -> B).andThen(g: (B) -> C): (A) -> C = { x -> g(this(x)) } infix fun <A, B, C> ((B) -> C).compose(g: (A) -> B): (A) -> C = { x -> this(g(x)) } ``` - `((A) -> B)` is the **receiver type** — the function you call the helper on. Inside the body, `this` refers to it. - The three **generic type parameters** `A, B, C` thread the types: input `A`, intermediate `B`, output `C`. The compiler infers them and rejects mismatched wiring. - The body returns a **lambda** `{ x -> g(this(x)) }`. Nothing executes yet — composition is **lazy**; the captured functions only run when you invoke the result. ## Using it ```kotlin val toLength: (String) -> Int = { it.length } val isEven: (Int) -> Boolean = { it % 2 == 0 } val lengthIsEven = toLength andThen isEven println(lengthIsEven("hello")) // 5 -> false ``` ## Key keywords - `infix` — enables `a andThen b` syntax. - extension function on a **function type** — the receiver is itself a function. - the returned **lambda captures** both functions (a closure).

  • What is the difference between `f andThen g` and `f compose g`?
    `f andThen g` runs f first then g (g(f(x))). `f compose g` runs g first then f (f(g(x))). They are mirror images of each other.
  • Does `andThen` run the functions immediately?
    No. It returns a new function and runs nothing. The composed functions only execute when you invoke the returned function with an argument.

Like an assembly line: andThen bolts the next station onto the end, so a part flows through each step in order.

saying these in an interview costs you the question

  • Says andThen executes the functions right away instead of returning a new function
  • Confuses the order: thinking compose runs the receiver first
  • Writes it as a top-level function taking two args instead of an extension on a function type
  • Forgets the generic type parameters so the intermediate type isn't threaded
  • Thinks Kotlin's standard library already provides andThen for (A)->B

context

open as a page

What is a higher-order function in Kotlin, and how do you declare a function that takes another function as a parameter?

level: juniorimportance: must knowfreq 85%

basics

~10 s

A higher-order function takes a function as a parameter or returns one. You declare the parameter with a function type like (Int) -> Int, then call it inside the body.

open as a page

Implement both `compose` and `andThen` as infix extensions on function types and explain why the generic signatures differ in argument order.

level: middleimportance: must knowfreq 45%

basics

~10 s

andThen runs the receiver first; compose runs the argument first. Their type parameters differ because the receiver and argument swap which function produces the intermediate value the other consumes.

open as a page

Why does idiomatic Kotlin usually prefer default and named arguments over currying or hand-rolled partial application? Give a concrete comparison.

level: middleimportance: must knowfreq 45%

basics

~20 s

Kotlin lets you give parameters default values and pass arguments by name, so you can omit or fix arguments at the call site without building chains of lambdas. It is clearer and avoids extra objects.

open as a page

What is currying in Kotlin, and how do you encode a two-argument function like add(a, b) as a curried function using returned lambdas?

level: juniorimportance: should knowfreq 35%

basics

~20 s

Currying turns a function that takes many arguments into a chain of functions that each take one argument and return the next function. You do it in Kotlin by returning a lambda from a lambda.

open as a page

How do you build a readable multi-step pipeline by composing function values, and how do method references (`::`) and bound references fit in?

level: middleimportance: should knowfreq 38%

basics

~10 s

Chain steps with andThen to form one pipeline function. You can plug in lambdas or method references like String::trim or obj::save so each step reads as a named operation.

open as a page

How do you implement partial application in Kotlin by capturing arguments in a closure, and how does it differ from currying?

level: middleimportance: should knowfreq 40%

basics

~10 s

Partial application means fixing some of a function's arguments now and getting back a new function that takes the rest. In Kotlin you do it by returning a lambda that captures the fixed arguments.

open as a page

When calling a higher-order function, what are the different ways to pass the function argument (lambda, reference, stored value), and how do nullable and receiver function types appear in a HOF signature?

level: middleimportance: should knowfreq 60%

basics

~10 s

You can pass a lambda, a function reference like ::name or obj::method, or a stored function value. A function-type parameter can be nullable, like ((Int)->Int)?, and can declare a receiver, like Int.()->String.

open as a page

How do you write a function that RETURNS a function in Kotlin, and what is captured when the returned lambda references the enclosing function's parameters?

level: middleimportance: should knowfreq 65%

basics

~10 s

Give the function a function-type return, like (Int) -> Int, and return a lambda. The lambda remembers (captures) the outer variables it uses, so each call can make a customized function.

open as a page

What is the runtime cost of composing functions with `andThen`/`compose`, and how do `inline` functions, closures, and lambda allocation factor into a senior's decision to use composition?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Each composition allocates a new lambda object and adds an extra call. It's usually cheap, but in hot loops the object allocations and indirection can matter, so measure before composing heavily.

open as a page

When you partially apply a function by capturing a mutable variable in a closure, what surprising behavior can occur, and how do you make the captured value stable?

level: seniorimportance: should knowfreq 22%

basics

~20 s

Kotlin closures capture variables by reference, not by snapshot. If you capture a var and later change it, the partially applied function sees the new value. Capture a val (or copy the value into one) to lock it in.

open as a page

Write a generic curry/uncurry pair for two-argument functions in Kotlin and explain the closures and types involved. What are the runtime costs?

level: seniorimportance: should knowfreq 25%

basics

~20 s

Write an extension that turns an (A, B) -> R into (A) -> (B) -> R by returning nested lambdas, and an uncurry that does the reverse. Each curried stage creates a small function object, so there's a tiny allocation cost.

open as a page

What is the runtime cost of passing lambdas to higher-order functions in Kotlin, and how does the inline modifier change that? Explain noinline and crossinline.

level: seniorimportance: should knowfreq 55%

basics

~20 s

Normally a lambda becomes an object, which costs an allocation. Marking the higher-order function inline copies its body and the lambda into the call site, removing that object. noinline and crossinline tweak which lambdas get inlined.

open as a page

Plain `andThen`/`compose` assume total, pure functions. How do you compose steps that can fail (return `Result`) or are nullable, and what are the pitfalls of naive composition with side effects?

level: seniorimportance: nice to knowfreq 22%

basics

~10 s

Basic compose blindly feeds output to input, so failures or nulls flow through unhandled. Use a fail-aware combinator that short-circuits, e.g. compose functions returning Result so the first error stops the chain.

open as a page

You are designing a public API. When should a parameter be a function type (HOF) versus a single-abstract-method interface, and what signature design choices make a HOF parameter pleasant to call?

level: principalimportance: nice to knowfreq 35%

basics

~20 s

Use a function-type parameter for simple one-shot callbacks so callers pass a quick lambda. Use a named interface when the callback has several methods, needs documentation, or must be implemented/reused. Put the lambda last so trailing-lambda syntax works.

open as a page