Reader small image

You're reading from  Kotlin Design Patterns and Best Practices - Third Edition

Product typeBook
Published inApr 2024
PublisherPackt
ISBN-139781805127765
Edition3rd Edition
Right arrow
Author (1)
Alexey Soshin
Alexey Soshin
author image
Alexey Soshin

Alexey Soshin is a software architect with 18 years of experience in the industry. He started exploring Kotlin when Kotlin was still in beta, and since then has been a big enthusiast of the language. He's a conference speaker, published writer, and the author of a video course titled Pragmatic System Design
Read more about Alexey Soshin

Right arrow

Introducing Functional Programming

This chapter will discuss the fundamental principles of functional programming and how they fit into the Kotlin programming language.

As you’ll discover, some of the concepts covered in this chapter have already been explored earlier in the book, as it would have been hard to discuss the benefits of the language up until now without touching on functional programming concepts such as data immutability and functions as values. We’ll look at these features from a different angle, though, and see how they fit into the wider functional programming paradigm.

In this chapter, we will cover the following topics:

  • Reasoning behind the functional approach
  • Immutability
  • Functions as values
  • Using expressions instead of statements
  • Recursion

After completing this chapter, you’ll understand how the concepts of functional programming are embedded in the Kotlin language and when to use them.

...

Technical requirements

There are no new technical requirements compared with the previous chapter.

You can find the code files for this chapter on GitHub at https://github.com/PacktPublishing/Kotlin-Design-Patterns-and-Best-Practices_Third-Edition/tree/main/Chapter05.

Reasoning behind the functional approach

Functional programming has been around for as long as other programming paradigms, for example, procedural and object-oriented programming. However, it has seen a notable surge in popularity over the past 15 years. One significant catalyst for this is the plateauing of CPU speeds. As we can’t ramp up CPU speeds like we used to (as transistors shrink to atomic scales, issues like quantum tunneling and other quantum effects start to interfere with their functionality), the alternative is to parallelize our software. It turns out that functional programming is particularly adept at handling parallel tasks.

The evolution of multicore processors is a fascinating topic in itself, but we’ll cover it only briefly here. Workstations have had multiple processors since at least the 1980s to support the running of tasks from different users in parallel. Since workstations were massive during this era, they didn’t need to worry...

Immutability

A cornerstone of functional programming is the concept of immutability. This dictates that an object remains unaltered from the time a function receives it as input until the function generates an output. You might wonder, how could the object change in the first place? To clarify, let’s examine a straightforward example:

fun <T> printAndClear(list: MutableList<T>) {
    for (e in list) {
        println(e)
        list.remove(e)
    }
}
printAndClear(mutableListOf("a", "b", "c"))

The code would initially output a, but then a ConcurrentModificationException would occur.

This happens because the for-each loop utilizes an iterator (a topic we discussed in the previous chapter). Modifying the list within the loop disrupts its operation. This leads us to ponder:

Wouldn’t it be wonderful to safeguard against such runtime exceptions from the get-go?

Next, we’ll discuss how immutable collections...

Functions as values

We’ve touched on Kotlin’s functional features in earlier chapters focused on design patterns. The Strategy and Command design patterns, for instance, lean heavily on Kotlin’s capacity to handle functions in different ways—like accepting them as arguments, returning them from other functions, storing them as values, or even including them in collections. In this section, we’ll explore additional facets of functional programming in Kotlin, including concepts like function purity and currying.

As we discussed previously, in Kotlin, it’s possible for a function to return another function. Let’s look at the following simple function to understand this syntax in depth:

fun generateMultiply(): (Int, Int) -> Int {
    return fun(x: Int, y: Int): Int {
        return x * y
    }
}

Here, our generateMultiply function returns another function that doesn’t have a name. Functions without a name are called...

Using expressions instead of statements

A statement is a block of code that doesn’t return anything. An expression, on the other hand, returns a new value. Since statements produce no results, the only way for them to be useful is to mutate the state, whether that’s changing a variable, changing a data structure, or performing some kind of IO.

Functional programming tries to avoid mutating the state as much as possible. Theoretically, the more we rely on expressions, the more our functions will be pure, with all the benefits of functional purity. This also improves the testability.

We’ve used the if expression many times already, so one of its benefits should be clear: it’s less verbose and, for that reason, less error-prone than the if statement from other languages. Let’s now see an alternative to if statements, called pattern matching.

Pattern matching

The concept of pattern matching will seem like switch/case on steroids. We&...

Recursion

Recursion is a function invoking itself with new arguments. Many well-known algorithms, such as depth-first search, rely on recursion.

Here is an example of a very inefficient function that uses recursion to calculate the sum of all the numbers in a given list:

fun sumRec(i: Int, sum: Long, numbers: List<Int>): Long {
    return if (i == numbers.size) {
        return sum
    } else {
        sumRec(i+1, numbers[i] + sum, numbers)
    }
}

We often try to avoid recursion due to the stack overflow errors that we may receive if our call stack is too deep. You can call this function with a list that contains a million numbers to demonstrate this:

val numbers = List(1_000_000) {it}
println(sumRec(0, 0, numbers))
// Crashed pretty soon, around 7K

However, Kotlin supports an optimization called tail recursion. One of the great benefits of tail recursion is that it avoids the dreaded stack overflow exception. If there is only a single recursive call in...

Summary

By now, you should have a more comprehensive grasp of functional programming, its advantages, and how Kotlin tackles this paradigm. We’ve explored the ideas of immutability and pure functions, and how their integration leads to code that’s both easier to test and maintain. Of course, nothing comes without some trade-offs. One significant drawback is that it can lead to performance issues in some scenarios due to the creation of numerous intermediate objects and the potential for increased memory usage.

Additionally, the paradigm shift from imperative programming requires a learning curve and can be challenging to integrate with imperative codebases.

We covered how Kotlin supports closures, allowing a function to access variables from its surrounding function and thus preserve state between multiple runs. This facilitates techniques like currying and memoization, which enable us to set default function arguments and cache previously computed function...

Questions

  1. What are higher-order functions?
  2. What is the tailrec keyword in Kotlin?
  3. What are pure functions?

Learn more on Discord

Join our community’s Discord space for discussions with the author and other readers:

https://discord.com/invite/xQ7vVN4XSc

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Kotlin Design Patterns and Best Practices - Third Edition
Published in: Apr 2024Publisher: PacktISBN-13: 9781805127765
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Alexey Soshin

Alexey Soshin is a software architect with 18 years of experience in the industry. He started exploring Kotlin when Kotlin was still in beta, and since then has been a big enthusiast of the language. He's a conference speaker, published writer, and the author of a video course titled Pragmatic System Design
Read more about Alexey Soshin