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
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Defining Functions

A function receives data as input and produces output, which may or may not depend on the input. Picture a coffee machine. The input is water and coffee beans, and the output is coffee:

A function can be something that receives a book as input and returns its price. More importantly, a function is a way to map items of one type into items of another type. Kotlin isn’t an FP language, but it contains different features that allow you to use many FP concepts.

Next, create a Functions.kt file and add the function below, which can be used to return the weight of a Book:

fun bookWeight(book: Book) = book.weight

In the same way you can create a function that returns the price of a Book. Add this code to Functions.kt:

fun bookPrice(book: Book) = book.price.value

In Kotlin, functions are first-class citizens and can be treated as data. This means that you can define the previous functions like this:

val bookWeightFun = fun(book: Book) = book.weight
val bookPriceFun = fun(book: Book) = book.price.value

Because functions are like objects, you can even assign them to a variable. Add the main() function below to the Functions.kt file:

fun main() {
  // 1
  var bookFun = bookWeightFun
  println("Book weight: ${bookFun(books[0])} Kg")

  // 2
  bookFun = bookPriceFun
  println("Book price: ${bookFun(books[0])} £")
}

Breaking down the above:

  1. The bookFun function variable is initialized using the bookWeightFun variable. Using the first book from the collection in Books.kt, bookFun will print out the book weight.
  2. Reassign bookFun to the bookPriceFun function and notice the price will now be printed.

Build and run. You’ll get an output like this:

Book weight: 2.1 Kg
Book price: 39.26 £

Function Types

When you define a variable, you’re implying that it can change its value. In the following code, you define the anotherBook variable, which can reference any instance of the Book class. The type of anotherBook is then Book. Add this code to a new file named FunctionTypes.kt:

var anotherBook = Book(
    "8850330731",
    "Android 3: Guida per lo sviluppatore (Italian Edition)",
    642,
    Price(40.06, "£"),
    1.8,
    2011,
    "Massimo Carli"
)

You can do the same with a function. In the following code, you define the anotherBookFun variable, which references the function you created earlier:

var anotherBookFun = fun(book: Book) = book.weight

You know that the anotherBookFun variable can also reference any other function of the same type. So, what is the type of a function?

Again, a variable’s type is a way to abstract all the possible values the variable can reference. In this case, those are functions like anotherBookFun and the previous bookFun. This is like a pipe with an input of type Book and an output of type Double. With Kotlin you can represent this type using this typealias definition. Add the following code:

typealias BookMapper<T> = (Book) -> T

This is a fundamental concept: The type of a function depends on the input and output types. In other words, what defines the type of a function is the set of possible values in input and the set of all the possible values in the output.

More importantly, the type of a function has nothing to do with how the mapping from the input and output value is done. That’s where the abstraction enters into play and where it becomes clear that FP has its own polymorphism.

To see this in action, copy this code into the FunctionTypes.kt file:

fun main() {
  // 1
  var mapper: BookMapper<Double> = ::bookWeight
  // 2
  var currency: BookMapper<String> = { book -> book.price.currency }
  // 3
  println("Weight of ${books[0].name} is ${mapper(books[0])} Kg")
  // 4
  mapper = ::bookPrice
  // 5
  println("Price of ${books[0].name} is ${mapper(books[0])}${currency(books[0])}")
}

Here’s what’s happening in the code above:

  1. Create a mapper variable, which references the function, which returns the weight of a book.
  2. The currency variable defines a function, which returns the currency for the given book as a string.
  3. Print the name of a book with its weight using the previously defined mapper variable.
  4. Update the mapper variable so it now refers to the function for the price of a book.
  5. Print the name and price of a book using the updated mapper variable.

Build and run. You’ll get an output like this:

Weight of Android 6: guida per lo sviluppatore (Italian Edition) is 2.1 Kg
Price of Android 6: guida per lo sviluppatore (Italian Edition) is 39.26£

Applying Higher-Order Functions

In the previous code, you assigned functions to variables as though they were normal objects. This is important to note because a function can be a parameter of another function or a return value. Kotlin developers are familiar with code like this, which can be added to a new HighOrderFunction.kt file:

fun List<Book>.total(fn: BookMapper<Double>): Double =
  fold(0.0) { total, book -> total + fn(book) }

This creates an extension function for the List<Book> that accepts as input a function that maps a book to the value to add to the total of type Double. This is a typical example of a function that accepts another function as a parameter. To understand how it works, add the following code into the HighOrderFunction.kt file:

fun main() {
  // 1
  val totalPrice = books.total { it.price.value }
  val totalWeight = books.total { it.weight }
  // 2
  println("Total Price: ${totalPrice} £")
  println("Total Weight: ${totalWeight} Kg")
  // 3
  books.forEach { println(it.name) }
}

Here, you:

  1. Use a lambda expression, pass the function parameter to the total function and calculate the total price and total weight of a list of books.
  2. Print the totals.
  3. Use a predefined higher order function of Kotlin in order to print all the names of the list of books.

Higher-order functions allow us to answer the first question from the section on FP: When you program using OO, you define classes. When you use FP, you create higher-order functions.

Build and run. You’ll get an output like this:

Total Price: 212.06 £
Total Weight: 9.700000000000001 Kg
Android 6: guida per lo sviluppatore (Italian Edition)
Android 3: Guida per lo sviluppatore (Italian Edition)
Sviluppare applicazioni Android con Google Play services (Italian Edition)
Creare la prima applicazione Android - Kindle
Android 4: Guida per lo sviluppatore (Italian Edition)
RoboGuice e Robotium: Dependency Injection applicata ad Android - Kindle
Android Activity: Gestire il flusso di navigazione di un'app  - Kindle
Sviluppare applicazioni per Android (Italian Edition)
Massimo Carli

Contributors

Massimo Carli

Author

Nick Winegar

Tech Editor

Nicole Hardina

Editor

Luke Freeman

Illustrator

Namrata Bandekar

Final Pass Editor

Eric Soto

Team Lead

Over 300 content creators. Join our team.