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

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

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
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)
}
10       // 1
null     // 2
10       // 3
Default  // 4

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
fun <A : Any, B : Any> A?.flatMap(fn: Fun<A, B?>): B? =
  if (this != null) fn(this)?.lift() else 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)
}
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
fun main() {
  reverse("supercalifragilisticexpialidocious") pipe ::println
}
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
}
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())
}
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
}
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
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
  }
fun main() {
  listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    .filterFold { it % 2 == 0 }
    .forEach(::println)
}
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
  }
fun main() {
  val list = List<Int>(37) { it }
  list.length() pipe ::println
}
37

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 }
fun main() {
  val list = List<Int>(37) { it }
  list.average() pipe ::println
}
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 }
fun main() {
  val list = List<Int>(37) { it }
  list.lastFold() pipe ::println
  val empty = emptyList<Int>()
  empty.lastFold() pipe ::println
}
36
null
fun <T> List<T>.firstFold(): T? =
  foldRight(null as T?) { item, acc -> item }
val list = List<Int>(37) { it }
list.firstFold() pipe ::println
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.
© 2024 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