Functional Programming with Kotlin and Arrow: Getting Started

In this tutorial, you will learn the fundamentals of functional programming and how various Kotlin language features enable functional programming concepts. By Massimo Carli.

Leave a rating/review
Download materials
Save for later

Fundamentals are, of course, fundamental — and fun! This is particularly true for functional programming (FP). You may already use FP in your Kotlin code. If you’re curious about what’s behind this important paradigm, this is the tutorial for you.

This functional programming tutorial dives deeper into the following topics:

  • Function types
  • Side effects
  • Higher-order functions
  • Abstraction and composition
  • The Arrow framework

Writing some code examples, you’ll learn how to “think functionally” and how FP can make your code more testable and safe. In order to go fast, we need to go well so let’s start!

Getting Started

First, click the Download Materials button at the top or the bottom of the page. You’ll see the initial and final code, but you’ll write most of it during this tutorial. Open the starter project in IntelliJ IDEA. In the Books.kt file, you’ll find the definition of the Book and Price classes:

data class Price(val value: Double, val currency: String = "$")

data class Book(
  val ISDN: String,
  val name: String,
  val pages: Int,
  val price: Price,
  val weight: Double,
  val year: Int,
  val author: String

You’ll also find a list of instances used to initialize the books property.

Just as Object Oriented Programming (OOP) means working with objects, Functional Programming (FP) means working with functions. This is the start of your journey from OOP to FP. Fasten your seatbelt!

OOP in a Nutshell

The main concept in OOP is the class because it’s the first construct that you can use to describe the makeup of objects — instances of that class — in terms of properties and operations.

To start, open Books.kt. Now, to create an instance of the Book class, invoke one of its constructors and assign the returned reference to an androidBook variable in main():

  val androidBook = Book(
    "Android 6: guida per lo sviluppatore (Italian Edition)",
    Price(39.26, "£"),
    "Massimo Carli"

Note how Book is not just a class but also defines a type. Book is a type of androidBook variable that can reference any other object of the same type. While an object is an instance of a specific class, its reference can be assigned to a variable of different types. Add this code at the end of main():

val obj: Any = androidBook
println("obj description: $obj")

This assignment is possible because a Book is an extension of Any. Stated another way, Any is an abstraction of Book.

Build and run. You should see the description for obj in the Run window.

obj name: Book(ISDN=8850333404, name=Android 6: guida per lo sviluppatore (Italian Edition), pages=846, price=Price(value=39.26, currency=£), weight=2.1, year=2016, author=Massimo Carli)

Types as Abstractions

Abstraction is the most important concept in software development. An alternative name for abstraction is subtraction. In this context, abstracting means including only what’s necessary.

The variable androidBook in the previous code is of the type Book because you’re interested in its properties such as name or price. When you use the obj variable of type Any, it means that you don’t care about the book properties. You just care about the fact that it is an object.

Abstracting Identity

Abstraction makes one object equal to another. A lion isn’t a tiger, but they’re equal if you think of them as carnivorous animals. In the same way, think of a type of a variable as a way of representing all the possible values that the same variable can reference.

Add the following code to main():

val myBook: Book = androidBook
println("myBook description: $myBook")

Here myBook is a Book which means it can contain, or reference, any element in the set of all possible books. A type, then, is a way to represent a set of values. A class describes the makeup of all its instances, and the type abstracts the set of all of them.

Build and run. The descriptions for obj and myBook match as expected.

obj description: Book(ISDN=8850333404, name=Android 6: guida per lo sviluppatore (Italian Edition), pages=846, price=Price(value=39.26, currency=£), weight=2.1, year=2016, author=Massimo Carli)
myBook description: Book(ISDN=8850333404, name=Android 6: guida per lo sviluppatore (Italian Edition), pages=846, price=Price(value=39.26, currency=£), weight=2.1, year=2016, author=Massimo Carli)


In creating a program, you define how objects interact with each other. Generally, objects are instances of classes you design for them to collaborate.

One of the main benefits of OOP is encapsulation, in which objects interact using their interfaces, or a set of operations that other objects see and can invoke. Unfortunately, objects are not very good in a multithreaded environment because they encapsulate what really matters — the mutable state.

Objects hide the way they mutate their states and, because they collaborate, they share data. Mutable and shared states are the primary cause of data races, which often result in bugs.


A data race happens when you have multiple threads accessing the same mutable state in an unsafe way. Even without FP, it’s possible to solve this problem by removing one of the causes.

A data race can’t exist if you have only one thread. Many systems use this Single Thread Model for UI management, allowing a single main thread — in this case, the UI thread — to access UI components.

Another option is not to allow mutable states. This is the case when you create immutable objects, which are thread-safe because they can’t change their state after creation.

Using immutable objects is a good practice in both OOP and FP. Your Book class is already immutable because of val instead of var in the definition of its properties.

To provide a mutable version of the same class, make it explicit defining the MutableBook class. Add the following code to Books.kt:

data class MutableBook(
  var ISDN: String,
  var name: String,
  var pages: Int,
  var price: Price,
  var weight: Double,
  var year: Int,
  var author: String

Moving From Objects to Functions

You might have some key questions about moving from objects to functions:

  • What’s the equivalent of a class in an FP world?
  • What’s the equivalent of an instance?
  • Can you use the same logical path you used with classes and objects?
  • What about types, abstraction and collaboration? Can a function be the solution to the data race problem you face in OOP?

Read on for the answers to these questions!

Massimo Carli


Massimo Carli


Nick Winegar

Tech Editor

Nicole Hardina


Luke Freeman


Namrata Bandekar

Final Pass Editor

Eric Soto

Team Lead

Over 300 content creators. Join our team.