Chapters

Hide chapters

Functional Programming in Kotlin by Tutorials

First Edition · Android 12 · Kotlin 1.6 · IntelliJ IDEA 2022

Section I: Functional Programming Fundamentals

Section 1: 8 chapters
Show chapters Hide chapters

Appendix

Section 4: 13 chapters
Show chapters Hide chapters

8. Composition
Written by Massimo Carli

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In Chapter 2, “Function Fundamentals”, you learned that category theory is the theory of composition, which is probably the most important concept in functional programming. In this chapter, you’ll learn why composition is crucial. In particular, you’ll learn how to:

  • Implement function composition in Kotlin.
  • Use curry and uncurry to achieve composition with multi-input parameter functions.
  • Implement partial application and learn why it’s useful.
  • Compose functions with side effects.
  • Handle mutation as a special case of side effects.

As usual, you’ll do this by writing Kotlin code with some interesting exercises and challenges.

Composition in Kotlin

In Chapter 2, “Function Fundamentals”, you implemented the function in Composition.kt:

inline infix fun <B, C, A> Fun<B, C>.after(
  crossinline f: Fun<A, B>
): Fun<A, C> = { a: A ->
  this(f(a))
}

after uses the Fun<A, B> typealias you defined like:

typealias Fun<A, B> = (A) -> B

Another way to see this is:

inline infix fun <A, B, C> Fun<A, B>.compose(
  crossinline g: Fun<B, C>
): Fun<A, C> = { a: A ->
  g(this(a))
}

To revise how they work, write and run the following code Composition.kt:

fun main() {
  val double = { a: Int -> a * 2 } // 1
  val square = { a: Int -> a * a } // 2
  val stringify = Int::toString // 3
  val stringifyDoubleSquareAfter =
    stringify after square after double // 4
  val stringifyDoubleSquareCompose =
    double compose square compose stringify // 5
  println(stringifyDoubleSquareAfter(2)) // 6
  println(stringifyDoubleSquareCompose(2)) // 6
}

Here, you:

  1. Define double as a function that doubles the Int passed in. This is a pure function.

  2. Define square as another pure function returning the square of the Int passed as input.

  3. Assign the reference of the toString function of Int to stringify.

  4. Use after to create stringifyDoubleSquareAfter as a composition of double, square and toString.

  5. Use compose to create stringifyDoubleSquareCompose as another composition of double, square and toString.

  6. Invoke stringifyDoubleSquareAfter and stringifyDoubleSquareCompose, passing the value 2 in input and printing the result.

When you run the code, you get:

16
16

The two functions return the same value, which isn’t as obvious of an outcome as it seems. This works because, as you learned in Chapter 2, “Function Fundamentals”, types and pure functions create a category and associativity is one of the three properties.

Compose multi-parameter functions

So far, so good. But in the previous example, you had very simple functions with one input parameter and one output parameter. How would you compose, for instance, the following functions? Copy these into Curry.kt to explore:

n*l y e e*9 e*9+s*j zor chfapqekj hboase leehqo
Xucixi 5.0: Xasvupexeem up pevdbuemm sovf dobqadyi istup magetufend

A generic curry function

In the previous section, you implemented a version of sum that receives an Int as input and returns a function of type (Int) -> Int that adds the new parameter value to the initial value. Now it’s time to implement curry as a generic function.

A practical example

As a more complex problem, you want to compose double, square, sum and stringify to achieve what’s in Figure 8.1 and represent the following expression:

Partial application

In the previous section, you learned how to use curry to compose functions with multiple input parameters. This is very useful, but, as you saw in Exercise 8.3, it can also be cumbersome in the case of many parameters. Additionally, most of the time, you don’t need to use just a single parameter at a time. To understand this concept, write this code in Partial.kt:

+sugxuzph(o: Teurga,v:Coabvi):Viugno Qupwixaguf «avbaknuni» +nemo (gowogs:Feogvi) FN «ascecnazu» Ziwbur «ucjasmovu» + hur(sdl: Yltuzs) +menruvbj ( u: Rienye, j:Koehre):Viipha ZofhuvagunZiqnacxfqpy «onjisr» DuqcaherexSoswenz «ihmalbibu» +yzauma(kr: XQ, falnif: Zilpuz):Dabnuboral «hquepap» «negedsv»
Litome 9.5: DebkolakaqGovdoml ufckinimyipoiz

Designing for partial application

As mentioned earlier, partial application is more powerful when the function has many input parameters. You understand how the order of the parameters is important. Just imagine you have a function of six parameters, and you’d like to partially apply just the first, third and last parameters. Using curry and flip is possible, but it would make the code unreadable. On the other hand, it’s very difficult to know how the function will eventually be partially applied, so what you can do is put the parameters:

Compose functions with side effects

What you’ve seen so far about composition involves pure functions, which are functions without any side effects and whose bodies are referentially transparent expressions. But what happens if the function isn’t pure because of some side effects? To understand what happens, start with a simple pure function and add some side effects later.

Side effects break composition

In Chapter 11, “Functors”, you’ll learn all about the map function. But to whet your appetite a little, map is a function that allows you to apply a function to all the elements in a container. To understand how it works, run the following code in the same SideEffect.kt file.

A composable effect

In the previous example, you proved that functions with side effects don’t compose well. One of the reasons is that composition means using the result of a first function as the input of the second. If the side effect isn’t part of the output, this makes composition difficult. What about removing the side effect from the body of the function and passing the same information as part of the value as output?

A common composition pattern

What you saw in the previous example is a common pattern in functional programming, and it works with different types of functions. Instead of handling composition for the type:

Currying again

Implementing compose for a specific type of function is a pattern you’ll see many times in this book, and in general, when you use functional programming. In the previous example, you learned how to compose a function with a particular side effect. The overloaded println function you used for printing Int values is a function of type (Int) -> Unit. You also used the overload of type (String) -> Unit. In any case, it’s a function with a String input and Unit as output. Open CurryAgain.kt and write the following code:

Compose mutation

In this final case of handling composition, it’s time to have some fun. The goal is to handle composition when the side effect of a function implies mutation. To understand how this works, open Mutation.kt and add the following code:

Composition with immutable objects

In the previous example, you used MutableCounter, but the good news is that you don’t have to change much if you want to use an immutable Counter. Open ImmutableComposition.kt, and add the following code:

Challenges

This is one of the most important chapters of the book because composition is the essence of functional programming. You already did some interesting exercises, so now it’s time for a couple challenges.

Challenge 8.1: Callable stuff

In this chapter, you learned how to implement the compose function in different scenarios following a common pattern. Consider, now, the following function type:

Challenge 8.2: Parameters or not parameters?

Suppose you have the following functions:

Key points

  • Composition is the most important concept of functional programming.
  • Category theory is the theory of composition.
  • Functions with a single input parameter are all you need. Using curry, each function with multiple parameters can be mapped into higher-order functions of a single parameter.
  • The name curry comes from Haskell Curry, an American mathematician and logician.
  • Partial application is a generalized version of currying. It allows you to decide what parameters to provide initially and what to provide later.
  • You can think of partial application as a way to implement dependency injection in a functional way.
  • The Kleisli category helps you understand how to implement composition of functions with side effects. The idea is to bring the effect as part of the return type, but this usually breaks composition.
  • Writer<T> leads you to a general pattern in the implementation of composition.
  • You can use the same pattern to manage composition of functions that contain mutation logic.

Where to go from here?

Wow! In this chapter, you’ve done a great job! Congratulations. Composition is probably the most fascinating part of functional programming and gives you a lot of gratification when you see your code compile and work.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now