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

I. Appendix I: Chapter 9 Exercise & Challenge Solutions
Written by Massimo Carli

Exercise 9.1

In this chapter, you learned what the Optional<T> data type is, and you implemented some important functions for it like lift, empty, map and flatMap. Kotlin defines its own optional type represented by ?. How would you implement the lift, empty and getOrDefault functions for it?

Exercise 9.1 solution

You can implement the previous functions like this:

fun <T : Any> T.lift(): T? = this // 1

fun <T : Any> T.empty(): T? = null // 2

fun <T : Any> T?.getOrDefault(defaultValue: T): T =
  this ?: defaultValue // 3

In this code, you define:

  1. lift as an extension function of the type T. Note that the receiver type T isn’t optional because of the constraint T: Any. The important thing here is that lift returns the same receiver but as a reference of the optional type T?.

  2. empty as an extension function of the not-optional type T, returning null, but as the value of a reference of type T?.

  3. getOrDefault also as an extension function of the optional type T?. Note how the return type is the not-optional type T. In the body, you just check if this is null, returning defaultValue if it is.

Run the following code for a better understanding of how all this works:

fun main() {
  val optStr = "10".lift() // 1
  optStr pipe ::println
  val empty = String.empty() // 2
  empty pipe ::println
  optStr                     // 3
    .getOrDefault("Default")
    .pipe(::println)
  empty                      // 4
    .getOrDefault("Default")
    .pipe(::println)
}

And you get the output:

10       // 1
null     // 2
10       // 3
Default  // 4

Here, you use:

  1. lift to convert a String, "10", into an optional String? you then print.
  2. empty to get null through a reference of type String? you also print.
  3. getOrDefault on optStr, getting 10 as the result.
  4. getOrDefault on empty, getting the default value Default you pass as a parameter.

Exercise 9.2

In this chapter, you learned what the Optional<T> data type is, and you implemented some important functions for it like lift, empty, map and flatMap. Kotlin defines its own optional type represented by ?. How would you implement the map and flatMap functions?

Exercise 9.2 solution

A possible implementation of map for the Kotlin optional type is:

fun <A : Any, B : Any> A?.map(fn: Fun<A, B>): B? =
  if (this != null) fn(this).lift() else null

This code has several important details:

  1. map is an extension function for the optional type A?.
  2. map accepts a single parameter of type Fun<A, B>.
  3. You return null if the receiver is null.
  4. If the receiver isn’t null, you pass it as an input parameter of the function fn and return the lifted result of type B?.

A possible implementation of flatMap for the Kotlin optional type is:

fun <A : Any, B : Any> A?.flatMap(fn: Fun<A, B?>): B? =
  if (this != null) fn(this)?.lift() else null

In this case, you note that flatMap:

  1. Is an extension function of the optional type A?.
  2. Accepts a single parameter of type Fun<A, B?>. It’s important to note the optional B? as a return type for the function, which differs from map.
  3. Returns null if the receiver is null or if fn returns null.
  4. Returns the result of invoking fn if the receiver isn’t null.

Exercise 9.3

How would you replicate the example you implemented in OptionalTest.kt using T? instead of Optional<T>? Use the solutions of Exercise 9.1 and Exercise 9.2 to implement this example.

Exercise 9.3 solution

You can test the code you created in Exercise 9.1 and Exercise 9.2 by running the following code:

fun strToInt(value: String): Int? = // 1
  try {
    value.toInt().lift()
  } catch (nfe: NumberFormatException) {
    null
  }

fun <T : Any> T?.getOrDefault(defaultValue: T): T = // 2
  if (this == null) defaultValue else this

fun main() {
  "10" // 3
    .lift()
    .flatMap(::strToInt)
    .map(::double)
    .getOrDefault(-1)
    .pipe(::println)

  "10sa" // 4
    .lift()
    .flatMap(::strToInt)
    .map(::double)
    .getOrDefault(-1)
    .pipe(::println)
}

In this code, you:

  1. Define strToInt as a function that converts a String into the Int it contains, if possible. If that isn’t possible, it returns null. This is a function of type Fun<String, Int?> you can pass as input to flatMap.
  2. Create getOrDefault, checking the receiver’s value and returning defaultValue if it’s null.
  3. Use the same structure you used with Optional<T> with a valid String.
  4. And again, use the same structure with an invalid String.

The output is:

20
-1

Exercise 9.4

Implement a function that reverses a String using one of the folding functions you’ve implemented in this chapter.

Exercise 9.4 solution

A String is just an array of Chars. This means that a possible implementation for the reverse function is:

fun reverse(str: String) =
  str.toCharArray().toList() // 1
    .declarativeFoldRight(StringBuilder()) { c, acc -> // 2
      acc.append(c) // 3
      acc
    }.toString() // 4

In this code, you:

  1. Convert the String passed as input to a List<Char>.
  2. Invoke declarativeFoldRight, passing a StringBuilder as the initial state for the accumulator.
  3. Append the character to the previous accumulator state in the combination function.
  4. Return the content of StringBuilder as a String.

To test the previous code, just run the following code:

fun main() {
  reverse("supercalifragilisticexpialidocious") pipe ::println
}

Getting:

suoicodilaipxecitsiligarfilacrepus

Exercise 9.5

In this chapter, you implemented declarativeFold and declarativeFoldRight as extension functions for List<T>. How would you implement them for Iterable<T>?

Exercise 9.5 solution

The folding functions work for any ordered collection of items, so what really matters is the ability to iterate over them. A possible implementation for declarativeFold on Iterable is:

fun <T, S> Iterable<T>.iterableFold(
  start: S,
  combineFunc: (S, T) -> S
): S { // 1
  tailrec fun helper(iterator: Iterator<T>, acc: S): S { // 2
    if (!iterator.hasNext()) { // 3
      return acc
    }
    return helper(iterator, combineFunc(acc, iterator.next())) // 4
  }
  return helper(iterator(), start) // 5
}

In this code:

  1. You create iterableFold as an extension function for Iterable<T>. The name is different from your previous implementations, so there are no conflicts.
  2. You define helper as a function accepting an Iterator<T>. In fact, you just need to check if you’re at the end of the Iterator<T> or not, which you do with hasNext.
  3. If you’re at the end of the Iterator<T>, you just return acc.
  4. Otherwise, you recursively call helper, passing the same iterator and the result you get combining acc with the next element.
  5. You start everything, invoking helper with the Iterator<T> you get from iterator. This is possible because the receiver is an Iterable<T>.

Using the same approach, you can also implement iterableFoldRight like this:

fun <T, S> Iterable<T>.iterableFoldRight(
  start: S,
  combineFunc: (T, S) -> S
): S {
  fun helper(iterator: Iterator<T>): S {
    if (!iterator.hasNext()) {
      return start
    }
    return combineFunc(iterator.next(), helper(iterator))
  }
  return helper(iterator())
}

To test how they work, run the following code:

fun main() {
  "supercalifragilisticexpialidocious".asIterable()
    .iterableFoldRight(StringBuilder()) { item, acc ->
      acc.append(item)
      acc
    } pipe ::println
  "supercalifragilisticexpialidocious".asIterable()
    .iterableFold(StringBuilder()) { acc, item ->
      acc.append(item)
      acc
    } pipe ::println
}

Getting:

suoicodilaipxecitsiligarfilacrepus
supercalifragilisticexpialidocious

Challenge 9.1: Filtering

How would you implement a filter function on a List<T> using fold or foldRight? You can name it filterFold. Remember that given:

typealias Predicate<T> = (T) -> Boolean

The filterFold function for a List<T> should have this signature:

fun <T> List<T>.filterFold(predicate: Predicate<T>): List<T> {
 // Implementation  
}

Challenge 9.1 solution

You know that fold allows you to basically recreate a collection of items. If you add an item after the evaluation of a predicate, you basically implement the filter function. One possible solution is:

fun <T> List<T>.filterFold(predicate: Predicate<T>): List<T> =
  fold(mutableListOf()) { acc, item -> // 1
    if (predicate(item)) { // 2
      acc.add(item) // 3
    }
    acc
  }

In this code, you:

  1. Invoke fold using a MutableList<T> as a starting value.
  2. Evaluate the predicate against the current value.
  3. Add the element if the predicate evaluates to true.

To test the previous code, simply run:

fun main() {
  listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    .filterFold { it % 2 == 0 }
    .forEach(::println)
}

And the output is:

2
4
6
8
10

Challenge 9.2

How would you implement the length function for a List<T> that returns its size using fold or foldRight?

Challenge 9.2 solution

A possible implementation is:

fun <T> List<T>.length(): Int =
  fold(0) { acc, _ ->
    acc + 1
  }

In this case, you don’t care about the items, but you increment acc for each of them. To test how this works, just run the following code:

fun main() {
  val list = List<Int>(37) { it }
  list.length() pipe ::println
}

And you get:

37

In this case, using fold or foldRight doesn’t make any difference.

Challenge 9.3: Average

How would you implement the avg function for a List<Double> that returns the average of all the elements using fold or foldRight?

Challenge 9.3 solution

The solution here is simple, and is basically the implementation of the definition of average: the sum of all the elements divided by the number of elements:

fun List<Double>.average(): Double =
  fold(0.0) { acc, item -> acc + item } /
      fold(0.0) { acc, _ -> acc + 1 }

Run this code to test the solution:

fun main() {
  val list = List<Int>(37) { it }
  list.average() pipe ::println
}

You get:

18.0

Challenge 9.4: Last

How would you implement the lastFold function for a List<T> that returns the last element using fold or foldRight? What about firstFold?

Challenge 9.4 solution

One possible implementation is:

fun <T> List<T>.lastFold(): T? =
  fold(null as T?) { _, item -> item }

In this case, it’s curious to see how the initial value matters only if the receiver is empty. Otherwise, only the last item matters. To test how it works, run this code:

fun main() {
  val list = List<Int>(37) { it }
  list.lastFold() pipe ::println
  val empty = emptyList<Int>()
  empty.lastFold() pipe ::println
}

Getting:

36
null

Note that to get the first element, you just need to use foldRight instead, like this:

fun <T> List<T>.firstFold(): T? =
  foldRight(null as T?) { item, acc -> item }

To test this, just run this code:

val list = List<Int>(37) { it }
list.firstFold() pipe ::println

And you get:

0
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.