Carli Massimo Functional Programming in Kotlin by Tutorials
Carli Massimo Functional Programming in Kotlin by Tutorials
IntelliJ IDEA Version 2022 or higher: This book uses IntelliJ IDEA Version
2022. While you can use other editors, this book shows examples using this
editor. You can download IntelliJ IDEA 2022 or higher from the JetBrains
website.
Android Studio Bumblebee or higher: There are a couple of chapters in
Section III that use an Android app for real-world examples. For those
chapters, this book expects you to open and build the project using Android
Studio. Download Android Studio Bumblebee (2021) or higher from the
Android developer website.
Kotlin 1.6: If you’re using IntelliJ IDEA and Android Studio, the editor comes
bundled with Kotlin already. If you’re choosing another editor, you can
download Kotlin at KotlinLang.org. This book assumes you have basic
knowledge of Kotlin. If you’re unfamiliar with Kotlin, start with Kotlin
Apprentice.
A readiness to learn! This book assumes you’re here to learn some exciting
things. Dive right in . :]
2
Functional Programming in Kotlin by Tutorials
https://github.com/raywenderlich/fpk-materials/tree/editions/1.0
Forums
We’ve also set up an official forum for the book at
https://forums.raywenderlich.com/c/books/functional-programming-in-kotlin.
This is a great place to ask questions about the book or to submit any errors you
may find.
3
Functional Programming in Kotlin by Tutorials
iii Dedications
Written by Massimo Carli
— Massimo Carli
4
Functional Programming in Kotlin by Tutorials
5
Functional Programming in Kotlin by Tutorials About the Team
6
Functional Programming in Kotlin by Tutorials
v Introduction
Written by Massimo Carli
You’ll take a pragmatic approach to learn how to make your code more:
Declarative
Robust
Error-proof
You’ll learn this and much more in Chapter 1. By the end of this book, you’ll
know how to apply pure functions, immutability and composition to simplify
your complex logic!
7
Functional Programming in Kotlin by Tutorials Introduction
Appendix
This book has many exercises and challenges throughout. In this appendix,
you’ll find solutions to all exercises and challenges, along with additional
explanations about the answers.
8
Functional Programming in Kotlin by Tutorials
1 Why Functional
Programming
Written by Massimo Carli
When you approach a topic like functional programming, the most common
questions are:
In this chapter, you’ll answer these and other questions about functional
programming. But first, consider these three main points:
This might surprise you, but you don’t need to know functional
programming to write your code. Many professional engineers have been
writing code for years without using any functional programming
techniques, which is totally fine. But in this chapter, you’ll learn that when
you use functional programming, your code will be more readable, robust,
reusable and testable.
Functional programming isn’t all or nothing. Gang of Four design patterns
you use with the object-oriented approach are still completely valid.
But what exactly does “better code” mean? The answer is complex, but in this
first chapter, you’ll get a taste of some of the principles that are the pillars of
functional programming:
Declarative approach
Higher-order functions
Composition
You’ll also have the chance to solve some fun and interesting exercises.
9
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
Suppose you have a list, List<String> . Some list elements are numbers, like
"123" . Others are normal String s, like "abc" . You want to implement a
function, stringSum , which returns the sum of all the values you can convert
from String to Int .
You can create a first solution to the problem and write it in the Declarative.kt
file in this chapter’s material:
2. Initialize the initial value to 0 for the sum to return as the result.
3. Use an enhanced for to iterate over all the values in List<String> . item
contains the current value at every iteration.
4. Try to convert the String to Int using toInt , adding it to the sum if you
can.
To test this solution, just add and run the following code using the list declared
above:
fun main() {
println("Sum ${imperativeSum(input)}")
}
Sum 1299
This is an imperative approach because you’re telling the program exactly what to
do and you’re doing this in its own language. Now, imagine you want to solve the
same problem, but explain it to a friend of yours in plain English. You’d just say:
This logic is closer to how you think, and it’s much easier to explain and
remember. But how can you translate it into code?
11
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
2. Use filter to remove the values that can’t convert to Int s from the
List<String> .
This code won’t compile because you still need to define isValidNumber , which
is a function you can implement like this:
Here, you just try to convert the String to Int and return true if successful
and false otherwise.
fun main() {
// ...
println("Sum ${declarativeSum(input)}")
}
Sum 1299
You might argue that in the declarative solution, you still use that ugly way of
testing whether String can be converted to Int . If String can be
converted, you also invoke toInt twice. You’ll come back to this later, but at
the moment, what’s important to note is that:
declarativeSum is written in a way that’s closer to how you think and not to
how the compiler thinks. If you read the code, it does exactly what you’d
12
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
describe in plain English. Filter out the String s you don’t want, convert
them to Int s and calculate the sum. The code is noticeably more readable.
Good code is also easier to change. Imagine you have to change the way you
filter String s. In imperativeSum , you’d need to add if-else s. In
declarativeSum , you just add a new filter , passing a function with the
new criteria.
Testability is a must in the modern software industry. How would you test
imperativeSum ? You’d create different unit tests, checking that the
function’s output for different input values is what you expect. This is true
for declarativeSum as well. But what you’d need to test is just
isValidNumber , as filter , map and sum have already been tested. You
really just need to test that the function isValidNumber does what you
expect.
Exercise 1.1: Implement the function sumInRange , which sums the values in
a List<String> within a given interval. The signature is:
Give it a try, and check your answer with the solution in Appendix A.
Higher-order functions
In this chapter’s introduction, you learned that the functions map and
flatMap — which you’re probably already using — are implementations of
some important functional programming concepts. In Chapter 11, “Functors”,
you’ll learn about map , and in Chapter 13, “Understanding Monads”, you’ll
learn about one of the most interesting concepts: monads. Monads provide
implementations for the flatMap function.
At this stage, it’s important to note how map and flatMap are examples of a
specific type of function: They both accept other functions as input parameters.
Functions accepting other functions as input parameters — or returning
functions as return values — are called higher-order functions. This is one of
the most significant concepts in functional programming. In Chapter 5,
13
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
“Higher-Order Functions”, you’ll learn all about them and their relationship
with the declarative approach.
As a very simple example, create a function called times that runs a given
function a specific number of times. Open HigherOrder.kt and add the
following code:
fun main() {
3.times { // 1
println("Hello") // 2
}
}
1. Invoke times as an extension function for the Int type. In this case, you
invoked it on 3 .
Hello
Hello
Hello
The code prints the “Hello” message three times. Of course, to make the times
function useful, you should make it work for all code you want to repeat. This is
basically a function accepting a lambda that, in this case, is a function of type
() -> Unit .
Note: The previous sentence contains some important concepts, like function
type and lambda, you might not be familiar with yet. Don’t worry — you’ll come
to understand these as you work your way through this book.
14
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
1. Define times as an extension function for Int . You also define a single
parameter fn of type () -> Unit , which is the type of any function
without input parameters and returning Unit .
2. Use a for loop to count the number of times related to the receiver.
3. Invoke the function fn you pass in input.
Now, you can run main , resulting in exactly what you expect as output: “Hello”
printed three times.
Like it or not, this implementation works, but it’s not very, ehm, functional. The
IntRange type provides the forEach function, which is also a higher-order
function accepting a function of a slightly different type as input. Just replace
the previous code with the following:
As mentioned, you’ll learn everything you need to know about this in Chapter 5,
“Higher-Order Functions”.
Give it a try, and check your answer with the solution in Appendix A.
Composition
As mentioned, functional programming means programming using functions in
the same way that object-oriented programming means programming with
objects. A question without an obvious answer could be: Why do you actually
need functions? In Chapter 2, “Function Fundamentals”, and Chapter 3,
15
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
“Functional Programming Concepts”, you’ll have a very rigorous explanation
using category theory. For the moment, think of functions as the unit of logic
you can compose to create a program. Decomposing a problem into smaller
subproblems to better understand them is something humans do every day.
Once you’ve decomposed your problem in functions, you need to put them all
together and compose them in the system you’ve designed.
Note: This also happens with objects. You use the classes as a way to model
the different components that collaborate to achieve a specific task.
Open Composition.kt in this chapter’s material and add the following code:
Both the functions map Int s to Int s, and you can represent them as
functions of type (Int) -> Int .
Composing double with square means invoking the first function and then
passing the result as input for the second. In code, this is:
fun main() {
val result = double(square(10)) // HERE
println(result)
}
Here, you invoke square by passing 10 in input. Then, you use the result to
invoke double . In that case, the output will be:
200
The question at this point is different. Because square and double are both
functions of type (Int) -> Int , you can assume that a third function exists:
squareAndDouble . It’s of the same type, (Int) -> Int , and does the same as
invoking square first and then double . Here, the input value doesn’t matter
16
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
anymore. You’re thinking in terms of functions. A simple — and obvious — way
to implement that function is the following:
This isn’t very interesting, though. In the previous section, you learned what a
higher-order function is. So now, the question is: Can you implement a higher-
order function that, given two functions as input, returns a third function that’s
the composition of the two? Yes, you can! Of course, the two functions need to
be compatible, meaning the output type of the first needs to be compatible with
the input type of the second. Besides that, you want a new function that creates
the composition of the other two functions. In Chapter 8, “Composition”, you’ll
learn all about this. At the moment — spoiler alert — you can define compose like
the following, which you should add in Composition.kt:
Note: Don’t worry if you don’t understand the previous definition. Teaching
how to write functions like this is the goal of the following chapters! :]
fun main() {
// ...
val squareAndDouble = ::square compose ::double // HERE
println(squareAndDouble(10))
}
That’s nice, but do you really need a compose function? Why not just invoke
functions the same way you did with square and double ? The answer is that
functional programming is magic. In this book, you’ll learn the main concepts of
category theory. You’ll also prove that using a functional programming
approach will make your code more robust, reusable and even more efficient.
Unfortunately, not all functions are like double and square . Other functions
17
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
aren’t so easy to compose and are impure. Functional programming is about
pure functions. But what are pure functions, and why are they so important?
Just to give you an idea, they’re functions whose work only depends on the
input parameter, and the function doesn’t change the world outside the
function itself when invoked. double and square are pure functions. They
return a value that depends only on the input value and nothing else. The
following, which you can add in Pure.kt, isn’t pure:
var count = 0 // 1
3. Increment count .
4. Use count and the input parameter value to calculate the value to return.
impure isn’t pure because the output doesn’t depend only on the input
parameter. Invoking impure multiple times with the same input parameter will
return different values.
Another example of an impure function is the following, which you can add to
the same file:
1. Define addOneAndLog of type (Int) -> Int , which just returns the value it
gets in input and adds 1 .
18
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
2. Calculate the incremented value and store it in result .
3. Use println to write a log message on the standard output.
If you invoke addOneAndLog multiple times with the same value in input, you’ll
always get the same value in output. Unfortunately, addOneAndLog isn’t pure
because the println changes the state of the world outside the function itself.
This is a typical example of a side effect. Impure functions are difficult to test
because you need to somehow replicate — using mocks or fakes — the external
world, which impure functions change. In the case of addOneAndLog , you’d
need to abstract the standard output, introducing complexity.
Now, you have bad news and good news. The bad news is that all the great
principles of functional programming you’ll learn in this book are only valid for
pure functions. The good news is that you’ll also learn how to make impure
functions pure.
How can you make addOneAndLog pure, then? A classic way is to move the
effect to become part of the result type. Replace the existing addOneAndLog
implementation with the following:
2. Returning a Pair of the result and the message you were previously
printing.
The first implementation of addOneAndLog had type (Int) -> Int . Now, the
type is (Int) -> Pair<Int, String> . What happens if you need to compose
addOneAndLog with itself or another function accepting an Int in input?
Adding the effect as part of the result type fixed purity but broke composition.
But functional programming is all about composition, and it must have a
19
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
solution for this as well. Yes! The solution exists and is called a monad! In
Chapter 13, “Understanding Monads”, you’ll learn everything you need to know
about monads, solving not just the problem of addOneAndLog , but all the
problems related to the composition of functions like that.
Exception handling
Exceptions are a typical example of side effects. As an example you should be
familiar with, open ExceptionHandling.kt and add the following code:
Even if it’s not visible in the function’s signature, the exception is a side effect
because it changes the world outside the function. Then, strToInt isn’t pure.
In the previous section, you already learned one way to make this function
pure: Move the effect as part of the return type. Here, you have different
options. The simplest is the one you get with the following code:
1. Define strToIntOrNull , which now has the optional type Int? as its
return type.
This function now is pure. You might argue that the try/catch is still there, so
it’s the exception. It’s crucial to understand that functional programming
doesn’t mean removing side effects completely, but it means being able to control
them. strToIntOrNull now returns the same output for the same input, and it
doesn’t change anything outside the context of the function.
In the case of strToInt , the information you want to bring outside is minimal:
You just want to know if you have an Int value or not. In another case, you
might need more information like, for instance, what specific exception has
20
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
been thrown. A possible, idiomatic Kotlin alternative is the following:
You’ll learn all about error handling in a functional way in Chapter 14, “Error
Handling With Functional Programming”. What’s important now is
understanding how even the existing Kotlin Result<T> is there as a
consequence of the application of some fundamental functional programming
concepts a model engineer should know to create high-quality code.
Key points
While object-oriented programming means programming with objects,
functional programming means programming with functions.
You decompose a problem into many subproblems, which you model with
functions.
A side effect is something that a function does to the external world. This
can be a log in the standard output or changing the value of a global variable.
Functional programming works for pure functions, but it also provides the
tools to transform impure functions into pure ones.
You can make an impure function pure by moving the effects to make them
part of the return value.
Error handling is a typical case of side effects, and Kotlin gives you the tools
to handle them in a functional way.
21
Functional Programming in Kotlin by Tutorials Chapter 1: Why Functional Programming
22
Functional Programming in Kotlin by Tutorials
2 Function Fundamentals
Written by Massimo Carli
Objects and functions are very different beasts. Objects, at first sight, probably
look closer to what you see and interact with every day. Think of your car, for
instance. It has some properties like brand, color, engine and size. You use
property values to distinguish a Ferrari from a Fiat 500. When you write your
code, you model objects using classes. For instance, you define a Car class
like this:
class Car(
val brand: String,
val color: Color,
val engine: Engine,
val size: Int,
var speed: Int
) {
fun drive() {
// Driving code
}
fun accelerate() {
speed += 1
}
}
The state of an object is the set of its properties’ values. Objects also interact,
sending messages to each other. This interaction happens by invoking
something called methods.
You can interact with a Car instance by invoking the accelerate method to
increase its speed, which then changes its state. Classes allow you to define what
the methods are. The concept of a class is just the first tool you have to describe
your application in terms of objects. Many other concepts and tools help define
what object-oriented programming is, such as interface, inheritance,
polymorphism and aggregation.
But what about functions? Are functions also close to what you see and interact
with every day? What does it really mean to program with functions? Is there
something similar to the concept of a class with functional programming?
23
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
These are all very theoretical concepts, but you’ll see some practical examples
using the Kotlin language.
Note: If you’re not confident with Kotlin, our Kotlin Apprentice book is the best
place to start.
Note: The concepts in this chapter might look theoretical and, sometimes, not
very intuitive. Don’t worry! It’s perfectly normal, and even expected, to read
this chapter multiple times to digest the content that will help you to better
understand many other concepts of functional programming in this book.
What is a function?
You might already have an initial idea about what a function is from the math
you learned in school. You may remember that a function is a way to describe
how to calculate an output given an input value, like in Figure 2.1:
x y = f(x) y
Returns an output value that’s double the value you pass in as an input. That’s a
representation of a mathematical function.
Note: It’s important at this point to emphasize the term bunch of values. This
is because the term set, as you’ll see soon, is something that gives some kind
of rules to that bunch. For instance, a set can include an object or not, and it
can’t include the same object more than once.
f(a)
f(b)
a’
f(c)
b’
Domain Range
For each value in the domain, there’s one and only one value in the range. This
implies that the function always returns a value for inputs in its domain.
The function f can map multiple values in the domain to the same value in
the range.
By definition, the function has meaning for each value in the domain, as
you’ll see later. This might seem obvious, but it’s a crucial concept to
understand.
25
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
Exercise 2.1: Can you write an example of a function mapping distinct values
in the domain to non-distinct values in the range, like f(b) and f(c) in
Figure 2.2?
Give it a try, and afterward, check the challenge project for a solution to see
how you did. You can find hints and an explanation of the solution in
Appendix B, “Chapter 2 Exercise & Challenge Solutions”.
Functions like twice mapping distinct values in the domain to distinct values
in the range have some interesting properties. As you’ll see later, the most
significant is that they have an inverse function. An inverse function maps the
values in the range back to the domain.
Exercise 2.2: Can you write the inverse function of twice ? What are the
domain and range for the inverse function? Check out the challenge project
and Appendix B for the solution.
Another simple and useful exercise is to define the domain and range for
twice in the previous example:
f(0)
f(1)
0 0
f(-2)
1 2
-2 -4
Int Int
26
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
Composition
Associativity
Identity
You can picture an object like a dot with no properties and no further way to be
decomposed. A morphism is an arrow between two objects like in Figure 2.4:
f1
a b
f2
27
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
Assign the names f1 and f2 to the morphisms between a and b and the
name g to the morphism between b and c .
Objects and morphisms are the primitive tools you can use to describe every
possible concept about category theory.
As mentioned earlier, objects and morphisms need to follow some rules that will
lead you to other fundamental functional programming concepts. It’s time to
study them in detail.
Composition
A bunch of objects and arrows don’t make a category; they also need to follow
some important rules, and composition is probably the most significant.
g°f
f g
a b c
The composition rule says that for every object a , b and c and every
morphism f and g , as shown in Figure 2.5, the category must have a
morphism from a to c . You represent this as g◦f between a and c ,
which is the composition of f and g . You read g◦f as “g after f”, where the
small circle ◦ represents the composition.
For a Kotlin example, if f is your twice and g is triple , then g◦f might
look like:
28
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
val `g◦f`: (Int) -> Int = { triple(twice(it)) }
Associativity
The associativity rule is similar to the one you studied back in school about
addition or multiplication. Look at the following image:
(h°g)°f
h°g
f g h
a b c d
g°f
h°(g°f)
The associativity rule says that for every object a , b , c and d and
morphism f , g and h (like in Figure 2.6), the following equivalence must be
true:
29
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
twice(triple(quadruple(x))) == quadruple(twice(triple(x)))
Identity
Identity is the last rule, but it’s no less important than the other two. Consider
the smiling diagram in Figure 2.8:
ia ib
a b
30
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
The identity property says that every object in a category must have at least one
morphism to itself. This is why, in Figure 2.8, you have:
A morphism f from a to b .
A morphism ia from a to itself.
timesOne(x) == x
It’s interesting to use the identity with the previous properties. You can easily
prove that, in a category, the following equivalence is true:
The concept of isomorphism at the end of this chapter will probably help, but
don’t worry — everything will be clearer when you get to Chapter 11,
“Functors”, and Chapter 12, “Monoids & Semigroups”.
Now that you know what a category is, it’s time for some fun — you’ll give some
meaning to objects and morphisms. Always remember that what’s true for a
category will also be true when you give objects and morphisms specific
meanings.
In the context of logic, assume that objects are propositions and morphisms
are entailments. Consider, then, Figure 2.10:
A B C
To prove it’s a category, you need to verify the three important rules:
Composition
Associativity
Identity
Proving composition
As you know, composition is the property that allows you to compose a
morphism from A to B and one from B to C into a single morphism from
A to C . In this case, if A ⇒ B and B ⇒ C , is it also true that A ⇒ C ? Try
to use the following propositions:
The fact that Alice drives to work every day entails she has a driver’s license.
That Alice has a driver’s license entails she’s at least 18. You then need to prove
the following: Is the fact that Alice drives to work every day entailing she’s at
32
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
least 18 years old? This is true, and this proves composition.
Note: In this example, you’re assuming Alice is in a place where you need to
be 18 years old to drive a car and vote. You might also object that this works
just for the previous propositions. In this case, you have two options: You can
just believe it or find a case where this isn’t true. :]
Proving associativity
To prove associativity, you need a new proposition like:
In this case, referring to Figure 2.10, you can use the following entailments:
From the definition of category, to prove associativity, you need to prove that:
(h◦g)◦f = h◦(g◦f)
(g◦f) = Alice drives to work every day. ⇒ Alice is at least 18 years old.
Proving identity
The final property to prove is very interesting and funny. In logic, you basically
need to prove that for every proposition A , you can say that A ⇒ A . This is
equivalent to saying that:
This actually has a name: tautology. :] This proves that logic is a category, and
you’ll use it to introduce two more crucial concepts: initial and terminal
objects.
33
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
A category has composition as a fundamental property. You can even say that
category theory is the science of composition. Isn’t decomposing concepts into
smaller ones what you do every day? Humans’ brains continuously decompose
concepts to make them simpler to understand and memorize, and then
recompose them. This might seem somewhat philosophical, but it’s proof that
category theory isn’t something completely unrelated to reality.
At this point, an example might help. Suppose somebody asks you to solve the
following addition problem:
7 + 10
Of course, you answer 17 . When you do that, are you getting the result from
your memory, or is your brain actually computing the addition of 7 with 10 ?
Frankly, it could be either. With this simple addition, your brain has probably
memorized the answer somewhere and is giving it as the answer.
Now, imagine somebody asks you to solve the following subtraction problem:
42 - 8
In this case, you probably don’t have the answer memorized, so your brain
needs to do some “computation”. Because it’s not an obvious answer, your brain
might do something like this:
42 - 2 = 40
40 - (8-2) = 40 - 6 = 34
42 - 2 - (8 - 2) = 34
Your brain has decomposed the simple subtraction into multiple operations that
are probably easier to calculate and then composed the results back into a
34
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
single value. This is an example of composition!
Note: Using the LEGO® analogy, you represent this concept saying that not all
the LEGO® pieces can be attached to any other piece.
To understand why this is, you need the definition of uniqueness. In this
context, you can say that the morphism f between the objects A and B is
unique if any other morphism g between the same objects cannot be different
from f . In short, if this happens, f and g must be equal.
With this definition in mind, you can define an initial object as an object with a
unique outgoing morphism to every other object in the category. A terminal object is
an object with a unique incoming morphism from any other object. Figure 2.11 gives
an idea of these concepts:
terminal
initial
Not all categories have initial and terminal objects but, if they do, they are
unique. In logic, the initial object has the name False and the terminal object
True.
35
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
Category properties have funny implications on these objects:
Each object has at least one identity morphism. This means that what is false
is false, and what is true is true.
Because there’s always a morphism starting from the initial object to any
other objects, there’s also a morphism from the initial object to the terminal
one. This means that a false assumption entails everything. Therefore, “Tom
has wings” entails “Tom can fly”, is a counterfactual implication.
You’re probably wondering what all this has to do with functional programming
— and Kotlin.
The good news is that anything you see that’s true for a generic category is also true for
a specific one. What’s true in logic is also true in programming when you give a
specific meaning to objects and morphisms. It’s now time to be more pragmatic
and start studying the most critical category for an engineer — spoiler alert — the
category of types and functions.
Exercise 2.3: Can you prove that using Set s as objects and “is a subset of” as
morphisms results in a category? In other words, a morphism from set A to
set B would mean that A is a subset of B . In that case, what are the initial
and terminal objects?
In the following paragraphs, you’ll answer these questions using the Kotlin
language, and you’ll have some revelations about some of the most important
Kotlin standard types. Here, using Kotlin, you’ll:
36
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
Prove that using types as objects and functions as morphisms, you define a
category.
See what initial and terminal objects mean when dealing with types and
functions.
Find out where the Kotlin types Unit and Nothing come from.
Composition
Associativity
Identity
Proving each of the category properties is also a good exercise to review some
interesting Kotlin concepts.
In the material for this chapter, you’ll find the starter project with some empty
files you’ll fill in along the way. Start by opening the Aliases.kt file and write the
following definition:
This is a type alias that represents all the functions from a type A to B . If A
and B are two objects of the category of types and functions, Fun<A, B> is a
morphism.
This simple definition allows you to prove each of the properties for a category.
Proving composition
To prove composition, you need to prove that for any function f from A to
B and any function g from B to C , there’s an equivalent function: g◦f
from A to C , which is the composition of the two.
This is nothing new to you because you’re probably composing functions every
day. Consider, for instance, the following code you can write in the Main.kt file:
37
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
1. Double the Int value it gets as input. The type of twice is Fun<Int, Int> .
You can compose twice and format in a very simple way, like this:
You can prove this by adding the formatAfterTwice definition in the Main.kt
file along with the following:
fun main() {
println(format(twice(37)))
println(formatAfterTwice(37))
}
When you run this code, you get the following output:
Result is 74
Result is 74
This is just an example, but proving that this works for any type and function
requires something more.
Open the initially empty Category.kt file in the project for this chapter, and add
the following definition:
Note: In the previous code you use the Kotlin keywords inline , infix and
crossinline . In this case, infix allows you to use a syntax like g after f
instead of g.after(f) . The inline keyword, as you’ll see in Chapter 4,
“Expression Evaluation, Laziness & More About Functions”, allows you to
basically replace every after invocation with the expression it represents.
Using inline then requires the use of crossinline for the input parameter,
f , in order to allow no-local returns from the function f itself.
38
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
This is a code representation of the (g◦f) notation you were using before,
where g represents this and f represents f .
Note: The last point asserts that after is a higher-order function because it
accepts a function as an input parameter and returns a function as output.
Don’t worry — you’ll learn about higher-order functions in Chapter 5, “Higher-
Order Functions”.
The definition of after looks more complicated than it actually is. Looking at
its body, it does exactly what you’ve done with formatAfterTwice but in a more
generic way. It:
Passes the result of f(a) as an input parameter for the function you use as
the receiver, which in this case has type Fun<B, C> .
You can now use after with the previous example. Just add the following code
to main in Main.kt with:
main() {
// ...
val f: Fun<Int, Int> = ::twice // 1
val g: Fun<Int, String> = ::format // 2
val formatTwice = g after f // 3
println(formatTwice(37)) // 4
// ...
}
Note: In the previous code, you used :: to reference a function using its
name without calling it. For instance, ::twice is a reference to twice .
39
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
Here, you:
Build and run main , and you get the following additional output:
Result is 74
Exercise 2.4: In this section, you defined after , which allows you to write
expressions like:
Can you write compose instead, which would allow you to implement the
same expression as:
Again, give it a try and check the challenge project and Appendix B to see
how you did.
It’s fundamental to note here the fact that after compiles is proof that
composition works for every type A , B and C and every function F<A, B>
and F<B, C> .
Proving associativity
To prove that the category of types and functions follows the associativity
property, you basically need to prove that:
40
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
Note: Here, you defined length explicitly, but you could use
String::length instead.
It’s a simple function that returns the length of the String you pass as an input
parameter. Its type is Fun<String, Int> .
fun main() {
// ...
val h: Fun<String, Int> = ::length // 1
val leftSide: Fun<String, Int> = (h after g) after f // 2
val rightSide: Fun<String, Int> = h after (g after f) // 3
println(leftSide(37) == rightSide(37)) // 4
}
2. Create leftSide as the left side of the equation you want to prove.
3. Define rightSide as the right side of the equation you want to prove.
4. Check that the two members are equal.
When you run main , you get the following additional output:
true
You might object again that this specific example doesn’t prove anything — and
you’re right! What actually proves that associativity works for the category of
types and functions is the successful compilation of after .
This is because it means that, given the types A , B and C and the functions
f and g , you can always create a function that is their composition.
(h after g) after f =
({ b: B -> h(g(b))}) after f =
{ a: A -> { h(g(f(a)))}}
41
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
h after (g after f) =
h after ({ a: A -> g(f(a))}) =
{ a: A -> { h(g(f(a)))}}
Note: In the last proof, you actually applied fundamental tools you’ll learn
about in detail in Chapter 3, “Functional Programming Concepts”.
Proving identity
What does it mean to prove identity for the category of types and functions? It
means to prove that, for every type A , there’s always at least one function of
type Fun<A, A> . To create such a function, open the Category.kt file and add
this code:
Although this function proves that identity exists, it’s important to mention that
it’s not the only one. The function twice you created earlier, for instance, is an
identity function because of type Fun<A, A> .
But what does it mean to say that twice is the inverse of half ? This means
that:
Note: In this case, if you half a value and then double it, you get the same
value. The same is true if you first double and then halve it.
When you have a function and an inverse function, you say that the functions
are isomorphisms. This definition gives a first reason for the existence of the
identity property. Of course, this isn’t true for all the functions from a type A to
42
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
a type B . Some functions don’t have an inverse.
Exercise 2.5: Can you write an example of an isomorphic function f and its
inverse g and prove they always compose to identity ? When you’re done,
look at the solution in Appendix B.
This concludes the proof that types and functions creates a category. What,
then, is the meaning of starting and terminal objects for the category of types
and functions?
In Kotlin, this initial object is Nothing . You might argue that you could always
write a function from Int to any other type, and this is true. The problem is
that the function must be unique.
If Int were a starting point, the following two different functions of type
Fun<Int, String> :
Would be the same function, but they aren’t! Instead, for any type A you
define:
43
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
This function’s name is absurd because, if you want to invoke it, you need a
value of type Nothing , which is impossible. IntelliJ gives you a hint for this:
Note: You might wonder why some of the functions have names like absurd
and others are anonymous, like f and g in the last example. The reason is
that anonymous functions cannot be generic.
You might try to invoke it like this, giving the Kotlin compiler some hint about
the generic type, but the result would be the same.
In the category of types and functions, this means a terminal object is a type
output of a unique function accepting an input parameter of any other type,
Nothing included. In Kotlin, this is the Unit type.
44
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
1. The unit function is generic with the type A , and it always returns
something you represent with Unit .
2. For any type A , the function unit is unique.
3. Unit isn’t just a type; it’s also the only value of that type.
At the beginning of the chapter, you learned that the concept of function isn’t
strongly related to the concept of computation. A function is a way to map
values in the domain to values in the range. In terms of mapping, unit and
unit2 are the same function.
Note: unit2 is actually a bad function because it hides a side effect, which is
another fundamental concept you’ll learn about in the next chapter.
The last point is also essential. You learned that with category theory, you can
only use objects and morphisms. What does it mean to say that Unit is the only
value for the Unit type? What does it mean that a is a value of type A ? In
general, what’s the relationship between the concept of type and the probably
more familiar concept of set?
var a: Int = 10
You usually read this as “ a is an Int ”, but what do you mean by Int ? If you
think about this, Int is just a name to represent the set of all the integer values.
a is of type Int means that you can assign to a only values that are in the
set you call Int .
45
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
Note: To be accurate, Int represents the set of all the possible integers that
you can represent using 4 bytes, so the whole numbers between
-2,147,483,648 (-2³¹) and 2,147,483,647 (2³¹-1).
In this case, you’re defining the variable s of type String , which is a way to
represent the set of all possible String s. Again, you can assign to s only
elements of the set you call String .
Thinking in terms of sets makes things easier. Consider the function twice
that you’ve already met:
This is basically a way to map elements in the set of Int to other elements in
the same set.
The function format is a way to map elements in the set of Int to elements in
the set of String .
twice(0)
twice(3)
0 0
twice(-2)
3 6
-2 -4
Int Int
46
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
between elements of two set s, which in this case are both Int . But what does
it mean to be an element of a set? How can you represent this using just objects
and morphisms?
De nition of element
As you learned earlier, objects in category theory are like dots; they cannot be
decomposed. This also means the morphisms are the only tool you can use to
distinguish one object from the others.
Nobody said that a terminal object can’t have outgoing morphisms. On the
contrary, from the terminal object, you might have many morphisms to a given
object A , like in Figure 2.15:
Terminal Object A
More importantly, you can give a different name to each morphism, like x , y
or z .
You need to understand what this means for the category of types and
functions. This means that for a given type A , there are many functions of type
Fun<Unit,A> , one for each value of type A .
As a simple example, consider the type Int . You can create a different
function of each value of Int , like these you can add to the Main.kt file:
47
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
A function for each element of the set you associate to the type Int and the
name you give to that function is an element of the set A represents.
fun main() {
// ...
// twice 2
val twiceTwo = ::twice after ::two // 1
println(twiceTwo(Unit)) // 2
// ...
}
When you run that code, you’ll get the expected output:
Nothing has a private constructor. This means that Nothing is a type, but you
can’t create any instances of it.
What about the terminal object? By definition, there’s a unique morphism from
any other type. This means there’s a unique function from any other type. If you
consider the terminal type to be a set, this means it’s a set containing a single
value that you know, in Kotlin, has the name Unit . Look at its source code, and
48
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
you’ll find this:
object Unit {
override fun toString() = "kotlin.Unit"
}
This means that Unit is a type, but it’s also the name for its unique instance.
Function types
In one of the previous paragraphs, you created the following definition:
With Fun<A, B> , you wanted to represent the set of all the functions from A
to B .
According to what you learned previously, this is exactly the concept of type of
a function. You’ve already learned how the category of types and functions
works. If the objects of that category are types of functions, what would the
morphisms be? This is something you’ll learn in the following chapter, and in
particular, with the concept of functor.
Challenges
In this chapter, you learned some fundamental concepts about category theory
and functional programming. It’s now time to solve some challenges and have
some fun. You’ll find the solutions in Appendix B, and the code in the challenge
project for this chapter.
49
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
What’s the domain and the range for this function? If you invoke oneOver(0) ,
you get an exception. How can you be sure you only pass values in the domain
as an input parameter?
Key points
A function is a way to map things from a domain to things in a range.
The category of types and functions is the most important for a software
engineer because it explains functional programming concepts.
The initial object in a category has outgoing unique morphisms to all other
objects.
The terminal object in a category has incoming unique morphisms from all
other objects.
Not all categories have a terminal object.
Initial and terminal objects explain the meaning of the Kotlin standard types
Nothing and Unit .
It’s useful to think of a type for a variable as a way to represent the set of all
the values that variable can reference.
Understanding the relationship between types and sets simplifies some of the
most critical functional programming concepts.
50
Functional Programming in Kotlin by Tutorials Chapter 2: Function Fundamentals
opportunity to see these concepts again in a more practical context. In the next
chapter, you’ll start to focus on pure functions.
Note: The inspiration for this chapter comes from the excellent work of
Bartosz Milewski in his YouTube video series Category Theory for
Programmers.
51
Functional Programming in Kotlin by Tutorials
3 Functional Programming
Concepts
Written by Massimo Carli
You also had the opportunity to learn the concept of type of a function. You
defined the typealias Fun<A, B> to represent all the functions mapping
values in A to values in B . For instance, Fun<Int, String> represents the
set of all the functions from Int to String . On the other hand, not all
functions are the same, and what you’ve seen so far is true for a specific type:
pure functions. You’ll learn more about this as you read on.
It’s also important to mention that all the functions you use in programming
contain some sort of computation, and the output is often calculated from the
input as the result of expressions.
In this chapter, you’ll learn some vital concepts about functions, and in
particular:
You’ll learn all these concepts using the Kotlin language and a bunch of
entertaining exercises and challenges.
52
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
Pure functions
In the previous chapter, you learned that a function is a way to map values from
a set A to values in a set B . You use sets to represent values of a specific type.
For functions from A to B , you defined the following typealias where A is
the type for the domain, and B is the type for the range:
Note: If you need a reminder about domain and range, skip back to Chapter
2, “Function Fundamentals”.
Note: You’ll find this definition already in the Aliases.kt file in this chapter’s
starter project.
Now, consider the following functions and add them to Pure.kt in the same
starter project:
In this code, twice and twiceAndLog have type Fun<Int, Int> , and you can
prove this by adding the following code to the same file:
fun main() {
var f: Fun<Int, Int> = ::twice // 1
println("Executing twice(10)")
f(10) // 2
f = ::twiceAndLog // 3
println("Executing twiceAndLog(10)")
f(10) // 4
}
53
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
4. Invoke f(10) .
Compile the code and run it. It provides the following output:
Executing twice(10)
Executing twiceAndLog(10)
2 * 10 = 20 // HERE
1. f always maps the same input values to the same output values.
2. The universe doesn’t change after each invocation of f .
The first point means that if you invoke the function f with specific input
parameters, you’ll always get the same output. In other words, the output of f
depends only on the input parameters and nothing else. Referential
transparency gives this concept a formal definition, and you’ll learn all about it
very soon.
The second property might look weird. If you consider the universe as a very big
object with some properties, it has a state. You can think of the universe as
everything else that exists outside the function’s body. As mentioned in the
introduction of Chapter 2, “Function Fundamentals”, the state of an object is
the set of all its property values — remember the Ferrari and Fiat 500?
More formally, you can now say that a function is pure if:
As mentioned, you’ll see what referential transparency and side effects are very
54
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
soon. For right now, it’s useful to see some examples and describe why pure
functions are so important in functional programming.
Note: The term “pure” comes from Lisp, a functional language that also
allows side effects. Functions with side effects aren’t considered to be good
functions, and so are somehow impure. :]
1. Every time you invoke twice with the same parameter value for x , you
always get the same result, x * 2 , in output.
2. It just returns an output value that is double the value you pass as input.
Nothing happens to the universe outside the function body, no matter how
many times it’s called.
To get an idea of the first property, add the following code to Pure.kt:
fun main() {
// ...
println(twice(10))
println(twice(10))
println(twice(10))
println(twice(10))
println(twice(10))
}
Note: The previous code just gives you an idea of referential transparency:
that the same input always maps to the same output. To be rigorous, you
should prove that the property is true for all the values in the domain of
twice and not just for the value 10 . :]
Here, you invoke twice(10) five times and, with println , you can verify the
result is always 20 . Run main and check the following output:
20
20
55
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
20
20
20
Now, follow the same process with twiceAndLog that you already wrote in the
Pure.kt file:
1. Every time you invoke twiceAndLog with the same parameter x , you
always get the same result, x * 2 , as output. As said, from this point of
view, twiceAndLog is the same function as twice .
2. Unfortunately, returning a result isn’t the only thing this function does. It
also changes the universe in the sense that the world now has some output
messages the function has written using println . The state of the universe,
specifically the standard output of your small program, is now different.
var count = 0
fun inc(x: Int): Int = ++count + x
Try arguing your answer and check Appendix C to see how you did.
This function adds a random integer to the value that was passed in. If you
check the two properties for a pure function, you see that:
1. Invoking randomInc multiple times with the same input parameter, you get
different output values because of the random value you get with
Random.nextInt() . This alone allows you to say that randomInc isn’t pure.
It’s worth checking the second property regardless.
2. Every time you invoke randomInc , you also change the universe. This isn’t
56
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
obvious because you don’t have any println , and apparently, you’re not
changing anything outside the function’s body. This actually isn’t true
because every time you invoke Random.nextInt() , you’re changing the
state of Random , the object responsible for generating random numbers.
Random works as a global object, which is part of the universe.
You can easily prove the first point by adding the following code to main in
Pure.kt:
fun main() {
// ...
println(randomInc(10))
println(randomInc(10))
println(randomInc(10))
println(randomInc(10))
}
If you run main , you’ll get different values like this as output:
424226219
-1433325033
-326412163
1941055914
Note: Of course, the values in your output will be different because of the
randomness of Random.nextInt() .
In Why are pure functions important?, you’ll learn how to recognize pure and
impure functions with other examples. But why is it so important to understand
if a function is pure?
val count = 0
fun inc2(x: Int): Int = x + count + 1
57
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
Easy to understand.
Easy to test and debug.
It’s worth going over each of these advantages with some small examples. You’ll
also have the chance to see each of these in depth in the rest of the book.
This function checks if x is negative, changing its sign if it is. Because x is all
you need, understanding what abs does is very simple. If this is too complex,
you could replace it like this:
The complexity of a function is in the interaction with the external world. These
interactions are called side effects. By definition, side effects aren’t present in
58
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
pure functions.
var count = 0 // 1
From what you learned earlier, this is impure because it changes the state of
the universe, and in particular, its property count , which is a mutable global
variable.
You’ll see in the rest of the book why this solution is far from being the best. But
in this case, you just need to understand whether or not it’s easy to test and
debug. To test it, create a test using IntelliJ. Select the name countedAbs and
press OPTION+ENTER to get the drop-down with a Create test option in Figure
3.1:
Select Create test and, after switching to JUnit4 in Testing Library, you’ll see
the following dialog:
59
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
class CounterKtTest {
@Test
fun test100Times() {
100.invokeTimes { // 1
countedAbs(it) // 2
}
assertThat(count).isEqualTo(100) // 3
}
}
1. Use invokeTimes , which is a simple utility method you’ll find in Util.kt. This
allows you to run the lambda you pass as a parameter the number of times
you use as the receiver. In this case, you invoke the lambda 100 times.
2. Invoke countedAbs , passing the current execution number as a parameter.
For this test, you don’t really care about the input parameter for
60
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
countedAbs .
If you run test100Times , you’ll get a green result, which means the test
succeeded. So far, so good.
Now, suppose you add another test like the following to the same
CounterKtTest.kt file:
class CounterKtTest {
//...
@Test
fun test50Times() {
50.invokeTimes {
countedAbs(it)
}
assertThat(count).isEqualTo(50)
}
}
If you now run the two tests, you’ll get some problems. In particular, you’ll get
something like this:
The specific test test100Times fails because you were expecting a count of
100 , but you get a count of 150 instead. The reason is obvious: The previous
test, test50Times , changed the universe and you didn’t run test100Times in
isolation like every test should be. In this case, the problem is that you need to
know what’s already happened to the universe to predict the result of your
function. What already happened depends on many things, like the number of
times the same countedAbs was already executed, making the test difficult to
implement.
You might argue that just resetting the value of count before each test, like the
following, could fix the problem:
@Test
fun test100Times() {
count = 0 // HERE
100.invokeTimes {
61
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
countedAbs(it)
}
assertThat(count).isEqualTo(100)
}
This is true, but only if all the tests aren’t run concurrently. In that case, this fix
wouldn’t work because of the risk of resetting the value of count in the middle
of the execution of another test.
How could you solve this problem, then? Would a pure version of countedAbs
solve this testability problem?
As you see, countedAbs now has two parameters: count is the number of
times the function has been invoked before this, and a is the input parameter
for abs . The return type is now very interesting because it’s a Pair<Int,
Int> . The first value is the result of abs , and the second is the new value of
count , which is the previous +1 . You can easily verify that countedAbs is
now a pure function:
What about the test, then? Open CounterKtTest.kt and replace the existing tests
with the following:
class CounterKtTest {
//...
@Test
fun test50TimesAbs() {
var c = 0 // 1
50.invokeTimes {
val (count, _) = countedAbs(c, it) // 2
c = count // 3
}
assertThat(c).isEqualTo(50) // 4
}
@Test
62
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
fun test100TimesAbs() {
var c = 0 // 1
100.invokeTimes {
val (count, _) = countedAbs(c, it) // 2
c = count // 3
}
assertThat(c).isEqualTo(100) // 4
}
}
2. Invoke countedAbs 50 times in the first test and 100 in the second, passing
the value you got from the previous execution for the count parameter.
Now, run the tests, and everything will pass. Consider also that the tests run in
complete isolation. Even if executed in a different thread, they’d be successful.
In this case, all of the changes are local to the scope of a single test.
Note: In the previous tests, you invoked countedAbs many times, but one
invocation would’ve been enough to test that it works! This is because this
version of countedAbs is pure. :]
var sharedCount = 1 // 1
Here, you:
63
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
Both comp1 and comp2 are impure because their output doesn’t depend just
on the input parameters but also on sharedCount . You can now compose them
like this using after , which you defined in the previous chapter and is found
in Category.kt:
Here, you defined comp2AfterComp1 , which is still impure because its output
doesn’t depend just on its input but still on sharedCount . If you have instead
composed two pure functions, the result would also be pure. In the rest of the
book, you’ll see many examples of pure functions composed of other pure
functions.
As you’ve read many times now, the output of a pure function just depends on
the input. Another way of saying this is that, with the input values, the function
has all it needs to return an output value. Pure functions don’t have any
dependency with the external universe and don’t share data with it. Pure
functions can run in parallel.
fun distance( // 3
x0: Double,
x1: Double,
y0: Double,
y1: Double
): Double {
val s1 = square(x1 - x0) // 4
val s2 = square(y1 - y0) // 4
return root(s1 + s2) // 5
}
64
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
1. square , which calculates the square of the Double value you pass as input.
This is a pure function.
2. root , which calculates the square root of the Double value you pass as
input. This is a pure function.
3. distance , which calculates the distance between two points given their
coordinates. This is also a pure function.
Look into the body of distance , and you’ll see that you:
4. Calculate the square of the difference for the x coordinates first and then
the square of the difference of y values and save them in s1 and s2 .
The interesting thing here is that the order you use to calculate s1 and s2
doesn’t matter. You could’ve calculated s2 first and then s1 . The important
thing is that you invoke sqrt when you have both s1 and s2 . Because the
order in which you calculate s1 and s2 doesn’t matter, it means you could
calculate them in parallel and then join the result when invoking sqrt . All the
options in Figure 3.4 are valid:
It is possible because square is pure. If it wasn’t pure and had side effects, the
result of square ‘s two executions would depend on what’s in the universe,
which also depends on the execution order.
The chance to run pure functions in parallel is also essential in the case of
distributed systems. You might run the functions on different machines,
providing just the input parameters and collecting the result.
65
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
Race conditions and deadlocks are two of the main problems when you run
functions in a concurrent environment. These especially happen when multiple
threads share mutable data. If a function is pure, it has all it needs in its input
parameters, so it doesn’t need to share anything.
Note: The code you execute as the body of a pure function must run in a
single thread.
The execution of a function can fail for many reasons. Imagine a function that
takes some time, and then, in the middle of the execution, the machine crashes.
Because the output depends on the input, you can simply retry the function’s
execution, and you’ll be sure to get the result you’re expecting. If the result is
very important, you could run the function on multiple machines and collect
the result from the first one that completes.
You’d always get the same result. The best solution, then, is to save the previous
result somewhere and return it if you invoke the function with the same input.
This process is called memoization.
66
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
There’s a definition saying that “a function is a way to describe some code you
can maybe run later”. This definition is apt because it makes explicit that you
can:
Completely avoid the execution of the function, saving time and resources.
Because pure functions don’t depend on the universe around them, you can
execute them at any time, wait to execute them, or even never execute them if
you never need the output value!
In the next chapter, Chapter 4, “Expression Evaluation, Laziness & More About
Functions”, you’ll learn more about laziness and why it’s an important feature
of pure functions.
val exp1 = 1 + 2 * 2 // 1
val exp2 = exp1 * 20 // 2
val exp3 = 2 * (expr2 - expr1) // 3
Here, you:
1. Define exp1 as the result of the sum of 1 and the result of the
multiplication of 2 with itself.
2. Reuse the result from the previous expression, exp1 , in a second one,
expr2 , which multiplies the previous value by 20 .
3. Reuse both the previously calculated expressions, exp1 and exp2 , for a
third, slightly more complex exp3 .
To get the result of exp3 , you basically reduce all the other expressions until
you get a single value. In the previous case, you calculate the result like this:
67
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
exp1 == 1 + 2 * 2 == 5
exp2 == exp1 * 20 = 5 * 20 == 100
exp3 == 2 * (expr2 - expr1) == 2 * (100 - 5) == 2 * 95 == 190
// 1
val a = expr
val (a1, a2) = a to a
// 2
val (a1, a2) = expr to expr
Is the definition in 1 equivalent to the one in 2? The answer is yes, but if and only
if expr is pure. To prove that, copy the following code into
ReferentialTransparency.kt:
fun main() {
val expr1 = { 3 * 10 } // 1
val (a1, a2) = expr1() to expr1() // 2
val expr1Eval = expr1() // 3
val (a1Eval, a2Eval) = expr1Eval to expr1Eval // 4
assertOrThrow("expr1 is not RT") { // 5
a1 == a1Eval && a2 == a2Eval
}
}
68
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
2. Create a Pair<Int, Int> evaluating expr1 for the first and second values.
5. Assert that the two Pair<Int, Int> are the same. You find assertOrThrow
in Ext.kt in the util package.
Run the code and see that there’s no exception thrown. This means the two
expressions are equivalent, so expr1 is referentially transparent. Can you do
the same with a referentially opaque function? To verify that, just add the
following code to main , which differs from the previous because it uses
expr2 , which isn’t referentially transparent. It changes an outside count
variable:
var count = 1 // 1
val expr2 = { 3 * 10 * ++count } // 2
val (b1, b2) = expr2() to expr2()
val expr2Eval = expr2()
val (b1Eval, b2Eval) = expr2Eval to expr2Eval
assertOrThrow("expr2 is not RT") {
b1 == b1Eval && b2 == b2Eval
}
In this case, the number of times you execute expr2 changes its output,
making it referentially opaque.
val expr3 = 42
val (a1, a2) = expr3 to expr3
69
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
Is this referential transparency? The answer is no, but if you add and run the
following code in ReferentialTransparent.kt, you don’t get any errors:
So, what’s wrong? The previous code just tests if the output of the expression is
always the same for the given input. In this case, there’s no input, and the
output is the output of println , which is Unit . To understand why expr3
isn’t pure, you need to check the definition.
Given:
Is the program:
Hello World!
Hello World!
Hello World!
70
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
In the second case, you’ve replaced the expression’s invocation with its result
value, and the program has changed. This proves that expr3 isn’t referentially
transparent.
This simple function calculates the cube of the input value. This is clearly a pure
function, and its body contains an expression that’s clearly referentially
transparent. Now, add the following new function to the same file:
71
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
fun doubleCube(x: Int) = cube(x) + cube(x)
This is another simple pure function calculating the sum of the values you get
from invoking cube twice. To calculate the result of doubleCube for 2 , do
the following:
doubleCube(2) ==
cube(2) + cube(2) ==
(2 * 2 * 2) + (2 * 2 * 2) ==
8 + 8 ==
16
You apply the substitution model and reduce the expression to the single value
16 . Because the expression in the body of doubleCube is referentially
transparent, you could replace every call to doubleCube(2) with the simple
value 16 .
Write and run the following code to verify that the result is 16 :
fun main() {
assertOrThrow("") {
doubleCube(2) == 16
}
}
It’s interesting now to look at the code Kotlin generates from the previous code.
In the Tools menu, go to the Kotlin submenu and select the Show Kotlin
Bytecode option, like in Figure 3.5:
72
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
Reading Kotlin bytecode can be made simpler with the Decompile button, so
press that now.
That leads to a more readable representation of the code the Kotlin compiler
generates from your source code, like in Figure 3.7:
Note: Don’t worry if you see some compilation errors in the decompiled code.
What you see here isn’t supposed to compile successfully, but it’s useful to
have an idea of how the Kotlin compiler works.
As you can see, the generated code isn’t much different from the one you wrote
initially. You still have:
73
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
2. doubleCube , which still invokes cube twice.
As said, cube and doubleCube are pure functions, and the expressions in
their bodies are referentially transparent. What about defining them as inline
functions? Change the previous implementations to include the inline
keyword. They should now look like this:
The only difference is the use of the inline keyword. If you now repeat the
process to get the Kotlin bytecode, this time removing what’s not critical, you’ll
get something like Figure 3.8:
Figure 3.8: The generated Kotlin bytecode decompiled for inline functions
As you can see, the Kotlin compiler has replicated the expression you wrote in
cube in every place where the same function has been invoked in
doubleCube . If you look at the generated code, you also note that:
2. The return value of doubleCube reuses var10000 and then another copy of
the expression in cube .
For a correct application of the replication model, the return value would’ve
been:
return x * x * x + x * x * x
You can say, then, that you can inline a pure function, but what’s really
happening isn’t a complete application of the substitution model. In addition,
74
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
when you look carefully at your source code, you’ll see a warning from IntelliJ,
like in Figure 3.9:
The message basically says the benefit in performance you get by inlining a
function like cube or doubleCube is insignificant and probably useless. The
message also says the possible benefits happen when you have parameters of
functional types. This means higher-order functions, which you’ll learn about
in detail in Chapter 5, “Higher-Order Functions”.
Side effects
You know that a pure function returns output values, which depend only on the
values you provide as input. The function calculates the output value, resolving
the referentially transparent expression you define in its body. You also learned
that a pure function doesn’t change the state of the universe. This means you
can run it an unlimited number of times without any external effect.
It’s very easy to fall into the trap of thinking that side effects are things you must
avoid completely. This isn’t actually true because side effects are the reasons you
create and run programs. For instance, if you want your app to persist some
data in a database or fetch some data by accessing the network, you need to
have some functions with side effects. Side effects are everywhere. So, what’s
the problem?
One of the problems you want to solve with functional programming is to control
side effects or isolate and execute them safely. As said, you’ll study side effects
much more in the following chapters, but it’s now important to give an example
of what it means to have control of side effects. Open SideEffects.kt, and add
the following code:
75
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
2. Negate the binary representation of the input value.
Using after , you can easily compose the two functions like this:
fun main() {
val comp1 = not(shiftLeft(10))
val comp2 = shiftLeftAndNot(10)
assertOrThrow("comp1 != comp2") {
comp1 == comp2
}
}
So far, so good! Now, suppose you want to log the operation you’re executing in
shiftLeft and not . How could you solve this problem in a functional way?
The new functions shiftLeftAndLog and notAndLog are not pure because
they have side effects! Each function is changing the universe by writing
something in the standard output. The side effects aren’t under control, and it’s
like each function is firing its own effect in a different direction.
The first consequence involves testability. How can you test shiftLeftAndLog
and notAndLog ? Trying to improve testability is a reason to improve the
previous example by introducing the Logger abstraction. Add the following
code in SideEffects3.kt:
76
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
interface Logger { // 1
fun log(msg: String)
}
1. Define Logger as the abstraction for the object responsible for logging.
2. Provide StdLogger as a Logger implementation for the standard output.
And test the previous functions by adding the following code to main in
SideEffects3.kt:
fun main() {
val mockLogger1 = MockLogger()
shiftLeftWithLogger(mockLogger1, 10)
assertOrThrow("Problem testing shiftLeft()") {
mockLogger1.log == "Shift Left of 10"
77
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
}
val mockLogger2 = MockLogger()
notWithLogger(mockLogger2, 10)
assertOrThrow("Problem testing not()") {
mockLogger2.log == "Negate 10"
}
}
Here, you’re basically trying to put the result of the side effect into some sort of
container, MockLogger , and verifying its content at the end of the test. Run
main and check that everything is fine without any exceptions.
How can you implement the initial shiftLeft and not implementations with
logging capabilities as pure functions, making them easier to test and compose?
In the previous examples, you implemented the side effect, invoking println
in the body of shiftLeftAndLog and notAndLog or delegating to a Logger in
shiftLeftWithLogger and notWithLogger . Now, use a different approach and
write the following code in SideEffects4.kt:
78
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
The idea is to include the description of the side effect in the return value of the
function. Both shiftLeftWithEffect and notWithEffect are pure!
Unfortunately they aren’t composable yet. The input for notWithEffect is Int ,
but the output of shiftLeftWithEffect is Pair<Int, String> .
Again, you’ll see these concepts in greater detail later, but now look at the
previous functions and their signatures. Define the following typealias :
You now have two functions of the same type, and you just have to define how
composition works for them. In the same SideEffects4.kt file, add the following
code:
2. Invoke the Writer<A, B> you pass as a parameter on the input a of type
A and, using Kotlin destructuring, save the result in two different local
variables: b of type B and str of type String .
3. Invoke Writer<B, C> using b from the previous expression, and
destructure the result in c of type C and str2 of type String .
4. Implement the actual composition, returning a Pair<Int, String> where
first is the value c you got from the previous instruction and second is
the concatenation of the String s you got in the previous instruction.
Now, by adding the following code in the same file, you can test everything:
79
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
fun main() {
val f: Writer<Int, Int> = ::shiftLeftWithEffect // 1
val g: Writer<Int, Int> = ::notWithEffect // 2
val shiftLeftAndNotWithEffects = g after f // 3
println(shiftLeftAndNotWithEffects(10).second) // 4
}
4. Print second in the final result, which is the concatenation of the logs.
Run the previous code, and you’ll get the following output:
Shift Left of 10
Negate 20
Note: Don’t worry if this process isn’t completely clear at this point. What
you’ve done in this paragraph is anything but obvious, and you’ll have many
opportunities to revisit the same concepts in the following chapters of the
book.
These are two functions of type Writer<Int, Int> , but they’re different from
80
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
the original ones of type Fun<Int, Int> you defined in SideEffects.kt:
Is there a way to get the former version from the latter? Of course there is! Write
the following function in SideEffects5.kt:
fun main() {
val shiftLeftAndLog = ::shiftLeft.liftW { a, _ -> // 1
"Shift Left $a"
}
val notAndLog = ::shiftLeft.liftW{ a, _ -> // 2
"Negate $a"
}
val shiftLeftAndNotAndLog = notAndLog after shiftLeftAndLog // 3
println(shiftLeftAndNotAndLog(10).second) // 4
}
81
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
2. Do the same for negate , creating notAndLog .
When you run this code, you’ll get exactly the same output you got earlier:
Shift Left 10
Negate 20
You did all this just using pure functions and composition!
Exercise 3.5: The Writer<A, B> data type you defined earlier is a very
important concept in functional programming. If you use types as objects and
the functions Writer<A, B> as morphisms, you get a very special category:
the Kleisli category.
Can you prove that by using types as objects and Writer<A, B> as
morphisms, you get a category? Look back at Chapter 2 if you need a
reminder for how to prove this.
Again, give it a try and check the solution in Appendix C to see how you did.
Challenges
In this chapter, you learned two of the most important concepts in functional
programming: referential transparency and side effects. You also learned how
to recognize a pure function. Now it’s time for a few challenges:
var count = 0
fun inc3(x: Int): Int {
val result = x + ++count + 1
println("Res: $result") // WHAT IF YOU REMOVE THIS?
--count
return result
}
82
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
Key points
A function f is pure if its body is a referentially transparent expression
and it doesn’t have side effects.
A pure function always returns the same output values for the same input
values.
A pure function depends only on its input parameters and nothing else.
Pure functions are easy to test and debug.
You can run pure functions in parallel.
Because the output of a pure function for a given input is always the same,
you can evaluate its expression only once and memoize it for following
invocations.
Pure functions are lazy. You can defer their execution until you really need
them.
An expression is referentially transparent if replacing every occurrence in
a program with its value doesn’t alter the behavior of the program itself.
The substitution model is a way to test if an expression is referentially
transparent.
An expression that isn’t referentially transparent is referentially opaque.
If the execution of a function changes the state of the universe, the function is
not pure, and you have side effects.
Side effects aren’t a problem per se, but you need to control them to reduce
the complexity of your program.
The Writer<A, B> data type is an example of how you can control side
effects.
83
Chapter 3: Functional
Functional Programming in Kotlin by Tutorials Programming Concepts
Using Writer<A, B> as morphisms and types as objects, you define the
Kleisli category.
You did a great job, but you’ve still got many concepts to learn — starting with
the concepts of strictness, laziness, immutability and tail recursion. You’ll learn
about how they’re related to functional programming and how they can help
you create better code.
84
Functional Programming in Kotlin by Tutorials
4 Expression Evaluation,
Laziness & More About
Functions
Written by Massimo Carli
In the previous chapters, you learned that you can define a function as “a bunch
of code that might be executed later”. You also learned several interesting
points:
As a software engineer, you write code and, more specifically, you write
functions.
If the functions you write are pure, their bodies are referentially transparent
expressions.
Executing a function means evaluating the expression in its body.
It’s possible that a function is never executed and the expression never
evaluated.
Evaluation strategy is the relationship between the position where you define
the expression in your code and the point when the expression is actually
evaluated. In this chapter, you’ll learn about two different evaluation strategies:
Applicative-order evaluation
Normal-order evaluation
These two evaluation strategies will lead you to other very important concepts
in this chapter:
Again, all this uses Kotlin and many engaging exercises and challenges. Open up
the starter project for this chapter to begin!
85
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
Suppose you want to evaluate the following expression that calculates triple the
average of two values:
3 * avg(x, y)
1. Create add , which returns the sum of the two input values.
2. Define triple , which uses add twice. This is possible because addition is
associative, as are pure functions. You also know that pure functions and
types create a category, which has associativity by definition.
3. Implement divide , which returns the division between the two input
values. This is a division of Int s, so the result will always round down to a
whole number. In this context, the precision of the function doesn’t really
matter, so that’s OK. :]
86
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
5. All these functions would be pure, but you added some println just to
understand how and, more importantly, when they’re evaluated. It’s
important to note how println follows the evaluation of the expression,
giving evidence of the actual execution.
fun main() {
triple(average(2, 4))
}
add
divide
average
add
add
triple
At first glance, you see that triple is the function evaluated last.
triple(average(2, 4)) // 1
triple(divide(add(2, 4), 2)) // 2
triple(divide(2+4, 2))
triple(divide(6, 2)) // 3
triple(6/2)
triple(3)
add(add(3,3), 3) // 4
add(3+3, 3)
add(6, 3) // 5
6+3
9
In the previous representation, you see that you always reduce the expression
that allows you to give the leftmost function all the value it needs. In particular:
87
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
4. It’s now time to evaluate triple , which needs to evaluate the add you have
as the first input parameter.
5. Finally, you reduce everything to the value 9 , which you can see by printing
the result of the expression using this code:
fun main() {
val result = triple(average(2, 4))
println(result)
}
But is this evaluation strategy good or bad? How would the same expression be
evaluated in the case of normal-order evaluation, and what would the
advantages be? Before exploring that, it’s important to see some more examples.
fun main() {
val inputValue = 3
if (inputValue > 4 && greaterThan10(inputValue * 2)) { // 3
println("OK") // 4
} else {
println("KO") // 4
}
}
88
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
whether the input value is greater than 10 .
KO
This is because inputValue isn’t greater than 4 . The interesting part is that
greaterThan10 wasn’t even invoked. The reason is the use of && , which is a
short circuit operator. When you resolve A && B , a false value of A makes
the whole expression false and the evaluation of B would be irrelevant.
Note: The same is true with the || operator. In the case of A || B , a true
value for A would make the whole expression true . The evaluation of B
would then be irrelevant.
fun main() {
val inputValue = 30 // HERE
if (inputValue > 4 && greaterThan10(inputValue)) {
println("OK")
} else {
println("KO")
}
}
greaterThan10
OK
In this case, inputValue > 4 evaluates to true and && requires the
evaluation of greaterThan10(inputValue) . This is why you see
greaterThan10 as part of the output.
So far, so good. This is how && works. Now, change the previous code like this:
89
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
fun main() {
val inputValue = 3 // 1
val greater10 = greaterThan10(inputValue) // 2
if (inputValue > 4 && greater10) { // 3
println("OK")
} else {
println("KO")
}
}
This is equivalent to the very first example but, when you run the code, you get
the following output:
greaterThan10
KO
The output contains KO , but greaterThan10 has been invoked anyway. This
might look obvious because you evaluate greaterThan10 before the test, and
you don’t know yet if the if condition would require it or not.
val empty = {}
90
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
In the case of an input parameter, you define a lambda expression like the
following:
In this case, you create an anonymous function that returns the sum of two
input parameters of type Int . The result of evaluating a lambda expression is
the value of the last expression in its body, in this case, the sum of a and b .
You separate the input parameters list and the result expression with -> . It’s
important to note that both the input parameter and the result expression are
optional, as you saw in the first example.
Exercise 4.1: Can you write a lambda expression that calculates the distance
between two points given their coordinates, x1, y1 and x2, y2 ? The
formula for the distance between two points is distance = √(x2−x1)²+
(y2−y1)² .
Give it a try, and afterward, check the solution in Appendix D or this chapter’s
challenge project.
When the compiler can’t get all the information it needs about types, you’re
supposed to help. You can give the compiler the information it needs in
different ways. In the previous code, you helped the Kotlin compiler by adding
explicit type information for a and b . The Kotlin compiler then understands
that a and b are Int s, and the result of the lambda expression will also be
Int because of the type of the result of a + b .
The following code would give a compilation error because the Kotlin compiler
wouldn’t know the types for a and b .
You can use a lambda expression as a normal value. This means you can assign
it to a variable, like in the following example:
91
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
By defining operation of type (Int, Int) -> Int , you’re telling the Kotlin
compiler the type of values you can assign to it. Because { a, b -> a + b } is
compatible with the type of operation , the compiler infers an Int type for a
and b and also verifies the return type. In this case, the Kotlin compiler
inferred the type of the input parameters a and b from the type of
operation you defined explicitly.
Exercise 4.2: What’s the type for the lambda expression you wrote in
Exercise 4.1?
Again, give it a try and check the solution in Appendix D or the challenge
project.
Exercise 4.3: What are the types of the following lambda expressions?
val emptyLambda = {} // 1
val helloWorldLambda = { "Hello World!" } // 2
val helloLambda = { name: String -> "Hello $name!" } // 3
val nothingLambda = { TODO("Do exercise 4.3!") } // 4
Try to answer without the support of IntelliJ, and check your solutions in
Appendix D or the challenge project.
92
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
Here, you’re defining a lambda expression that describes how to calculate the
addition of two Int s, but you’re not actually running it. This is the beauty of
lambda expressions: They allow you to describe code you’ll execute later with
the advantage that you can treat them as data. You can handle the value 2 and
the lambda operation as values exactly the same way. You can treat them as
input parameters and return values of functions.
Eventually, you might need to execute or resolve the lambda expression. How
can you do that? And, in that case, what would the final result be?
fun main() {
val result = operation(3, 4)
println(result)
}
You can achieve the same thing using invoke like this:
fun main() {
val result = operation.invoke(3, 4)
println(result)
}
The value you get resolving a lambda expression is the value of the last
expression in its body. Consider the following code:
What are the actual lambdas you can assign to it? Only the ones of type (Int,
Int) -> Int ? In this case, the answer is yes. You can only assign functions of
that type to operation because Int is final, but this isn’t always the case.
Note: Saying that Int is final means the Int class can’t be extended.
class A
class B
94
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
LambdaType is the type of generic function from A to B . This means you can
write the following code, which is perfectly legal:
class C
class D
What relation do these new types need to have with the previous A and B to
make the following definition valid?
To fix this, you need to create the following relations you can visualize in Figure
4.1:
class A : C()
open class C
class D : B()
open class B
This means LambdaType is contravariant for the input type and covariant for
the output type.
95
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
Note: Variance is a crucial concept when working with generic types. Given a
hierarchy relation between two types, A and B , it describes the relation
between F<A> and F<B> . Here, F is a data type you represent using
generic types like List<T> , Set<T> and others. Using this terminology:
You have covariance when given B IS-A A entails that F<B> IS-A F<A> .
When given B IS-A A entails that F<A> IS-A F<B> , you have
contravariance.
When given B IS-A A entails nothing about F<A> and F<B> , you have
invariance.\
Well, in this specific example, there’s no difference — but consider the following
case instead:
interface Combinable<A> { // 1
fun combine(rh: A): A
}
96
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
How would you represent combine(lh: A, rh: A) using a lambda expression?
Unfortunately, the following definition wouldn’t work:
You can’t define a generic lambda expression the same way you do for a normal
generic function. You might try to solve this problem with the following code:
typealias CombineLambdaType<A> =
(Combinable<A>, Combinable<A>) -> Combinable<A> // 1
This doesn’t work because you wouldn’t be able to set the constraint on the type
A .
In short, you can’t define a lambda expression on a generic type. This is why
expressing combine(lh: A, rh: A) using a lambda expression won’t work. It’s
the consequence of the fact that lambda expressions are like values, and you
can’t define a value of a generic type A .
Lazy evaluation
Now that you know almost everything about lambda expressions in Kotlin, you
can return to the following example you already wrote in ApplicativeOrder.kt.
Next, copy it into LazyEvaluation.kt, using the same greaterThan10 :
fun main() {
val inputValue = 3
val greater10 = greaterThan10(inputValue)
if (inputValue > 4 && greater10) {
println("OK")
} else {
println("KO")
}
}
97
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
greaterThan10
KO
How can you keep the same layout and evaluate greaterThan10 only when you
really need to? Now you know the answer: Use a lambda expression. Just
replace the previous code with the following:
fun main() {
val inputValue = 3
val greater10 = { greaterThan10(inputValue) } // 1
if (inputValue > 4 && greater10()) { // 2
println("OK")
} else {
println("KO")
}
}
KO
Running the same code after changing inputValue ’s value to 30 , you get:
greaterThan10
OK
Exercise 4.4: Can you implement a function simulating the short-circuit and
operator with the following signature without using && ? In other words, can
you replicate the short-circuiting behavior of left && right :
Can you also write a test to prove that right evaluates only if left is false?
Try to answer without the support of IntelliJ, and check your solutions in
Appendix D or the challenge project.
98
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
fun testDelegate() {
var variable by object { // 1
99
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
Now that you know how by works, just write the following code:
fun main() {
val inputValue = 30
val greater10 by lazy { greaterThan10(inputValue) } // 1
if (inputValue > 4 && greater10) { // 2
println("OK")
} else {
println("KO")
}
}
1. Use the Kotlin keyword by to delegate the access to the greater10 variable
to the object you get by invoking lazy . You pass to lazy the lambda
function you want to eventually execute for greater10 initialization.
2. Just access greater10 in the if condition.
greaterThan10
OK
KO
This is what you’re expecting, but lazy gives you something more. Add the
following code to Lazy.kt:
fun multiLazy() {
val multiLambda by lazy { println("I'm MultiLambda") }
multiLambda
multiLambda
multiLambda
multiLambda
}
100
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
2. Access multiLambda multiple times.
I'm MultiLambda
Of course, in this example, you use a println to prove that behavior, but if it
doesn’t have any side effects or the expression takes some time, lazy is very
useful. In this way, you achieved memoization, which you’ll learn about next.
Exercise 4.5: Can you implement the function myLazy with the following
signature, which allows you to pass in a lambda expression and execute it
just once?
Try to answer without the support of IntelliJ, and check your solutions in
Appendix D or the challenge project.
Memoization
In the previous chapter, you learned that a pure function always produces the
same result for the same input values, a concept known as referential
transparency. A pure function also doesn’t have any side effects, and this
makes it idempotent. Because of this, you don’t need to run the same pure
function multiple times! You simply run it once and store the result for later
invocations, saving time at the cost of some memory. Of course, the advantage is
bigger if the computation takes some time.
You already learned how to implement memoization with Kotlin using the
lazy function. It would be a good exercise to implement memoization for any
function of type Fun<A, B> . How would you implement it?
Note: You can try to solve this problem on your own as a fun exercise before
looking at the proposed solution. :]
Open the initially empty Memoization.kt file and write the following code:
101
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
fun <A, B> Fun<A, B>.memo(): Fun<A, B> { // 1
val cache by lazy { mutableMapOf<A, B>() } // 2
return { a: A -> // 3
val cached = cache[a] // 4
if (cached == null) {
cache[a] = this(a)
}
cache[a]!! // 5
}
}
1. Define memo as an extension function for Fun<A, B> . The return type is
still Fun<A, B> . Remember Fun<A, B> is an alias of the type (A) -> B .
2. Use lazy to define MutableMap<A, B> , which will store the values to cache.
In this case, lazy allows you to create just one instance of the cache in a
lazy way.
4. Check in the cache to see if you already have a result for the given input a .
If not, you invoke the function and store the result.
5. Now you have the result, cached or not, to return.
fun main() {
val testFunction = { a: Int -> println("Evaluating... $a"); a * 2
} // 1
println("Running testFunction 4 times")
testFunction(2) // 2
testFunction(2)
testFunction(2)
testFunction(3)
val memoTestingFunction = testFunction.memo() // 3
println("Running memoTestingFunction 4 times")
memoTestingFunction(2) // 4
memoTestingFunction(2)
memoTestingFunction(2)
memoTestingFunction(3)
}
1. Define a simple testFunction that returns double the input Int , printing
a message to give evidence of the actual invocation.
2. Invoke testFunction four times. The first three times, you use the input
2 , passing 3 instead in the last invocation.
102
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
3. Use memo to get the memoized version of testFunction you save in
memoTestingFunction .
As you see, you have an output message every time you invoke testFunction .
When you invoke memoTestingFunction , you have a message only the first time
you pass a specific input value. This is exactly what you expect from memo .
In this simple function, you create a List<Int> using the constructor List
provides. It requires the size of the List and a lambda expression invoked
for each element, which returns the value given its index. So far, so good. This
solution has some problems, though:
1. The eagerEvenSequence function returns a List with all the values you
might need. Here, the “might” is important because you might not need all of
them.
2. If you just need the last value, you have to get the complete List first
anyway.
3. If you need more values, you must invoke eagerEvenSequence with a new
input value.
103
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
fun main() {
println(eagerEvenSequence(5))
}
[0, 2, 4, 6, 8]
In this case, the example is very simple, but in other cases, it might be useful to
get a value of the sequence only if you really need it and when you really need it.
2. Initialize count to -2 . This weird initial value allows you to make the
following line shorter and still return 0 as the first value after you add 2 .
3. You return a lambda expression that adds 2 to count and returns the new
value.
You can test the previous code by adding this to the same file:
fun main() {
val evenSequence = evenPositiveStream() // 1
5.times { // 2
println(evenSequence())
}
}
In main , you:
2. Use the times utility function you find in Utils.kt to invoke evenSequence
five times.
104
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
0
2
4
6
8
More importantly:
1. You can persist the values of the sequence only if you really need them.
2. If you need more values, just invoke evenSequence more times.
Note: You might argue that in the case of eagerEvenSequence , you could just
return double the input parameter. This is true, but, as the next exercise
proves, you’re not always so lucky. :]
This is a very simple example of how lazy evaluation with lambda expressions
allows you to create sequences in an optimal way.
Exercise 4.6: Can you create a function fibo returning the values of a
Fibonacci sequence? Remember, every value in a Fibonacci sequence is the
sum of the previous two elements. The first two elements are 0 and 1 . The
first values are, then:
0 1 1 2 3 5 8 13 21 ...
Try to answer without the support of IntelliJ, and check your solutions in
Appendix D or the challenge project.
Normal-order evaluation
Note: The following two sections are optional. They give you some
information about the normal-order evaluation used by languages like
Haskell, in case you’re curious to know more. If you’re not curious, you can
skip right to the challenges.
At the beginning of the chapter, you learned that Kotlin evaluates all
expressions using applicative-order evaluation. This means it evaluates the
expressions you pass as input parameters of a function before the function
itself. After that, you also learned that Kotlin allows you to implement lazy
evaluation using lambda expressions. Some other languages, like Haskell, use
105
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
normal-order evaluation instead. But what does that mean, and is it possible to
simulate normal-order evaluation with Kotlin?
3 * avg(x, y)
You want to evaluate the function before the expression you pass as a
parameter. Open NormalOrder.kt and add the following:
The input parameters are not executed expressions but lazily executable
lambda expressions.
You don’t evaluate the expression you use as the input parameter before the
evaluation of addL but as part of it.
You can now apply the same logic to the following functions. Add these to the
same NormalOrder.kt file:
106
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
fun main() {
tripleL(averageL({ 2 }, { 4 }))()
}
tripleL
addL
addL
averageL
divideL
addL
averageL
divideL
addL
averageL
divideL
addL
Try to mimic this by applying the substitution model. In this case, you have a
longer sequence than you did earlier in this chapter because you evaluate the
external function on the left first.
tripleL(averageL({2},{4}))()
addL(addL(averageL({2},{4})),averageL({2},{4}))),averageL({2},
{4})))()
addL({averageL({2},{4})()+averageL({2},{4})()}(),averageL({2},{4}))
()
{{averageL({2},{4})()+averageL({2},{4})()}()+averageL({2},{4})()}()
{{{divideL(addL({2},{4}),2)}()+averageL({2},{4})()}()+averageL({2},
{4})()}()
{{addL({2},{4})()/2}()+averageL({2},{4})()}()+averageL({2},{4})()}
()
{{addL({2},{4})()/2}()+averageL({2},{4})()}()+averageL({2},{4})()}
()
{{(2+4)/2}()+averageL({2},{4})()}()+averageL({2},{4})()}()
{{(2+4)/2}()+{{divideL(addL({2},{4}),2)}()+averageL({2},{4})()}()
{{(2+4)/2}()+{addL({2},{4})()/2}()+averageL({2},{4})()}()
{{(2+4)/2}()+{(2+4)/2}()+averageL({2},{4})()}()
{{(2+4)/2}()+{(2+4)/2}()+{{divideL(addL({2},{4}),2)}()}()
{{(2+4)/2}()+{(2+4)/2}()+{addL({2},{4})()/2}() }()
{{(2+4)/2}()+{(2+4)/2}()+{(2+4)/2}()}()
{3+3+3}()
{9}()
As you can see, the leftmost function is the one you’re evaluating in each step.
107
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
define your expression and when to evaluate it. If you use the Kotlin lazy
function, you also get the benefit of memoization, which is very good in the case
of expensive — and pure — functions.
In particular, using the Kotlin lazy function, you get three different
synchronization modes for free through the LazyThreadSafetyMode enum
class. With this, you can decide how a lazy instance synchronizes initialization
among multiple threads. Possible values are:
In the last example, you might have the impression that normal-order evaluates
the same expressions multiple times, which is a problem that lazy evaluation
solves elegantly. On the other hand, applicative-order evaluation doesn’t always
work well with recursion. If the function you pass as a parameter is recursive, it
might not end. Or the expression could never return, like in this example you
can write in EvalExamples.kt:
fun main() {
double(neverEnding()) // 3
}
108
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
Challenges
In this chapter, you learned many important concepts about expression
evaluation, lambda functions and lazy evaluation. You’ve already done some
interesting exercises, but here you have some additional challenges. Have fun! :]
Can you create a sequence that provides the sum of the n terms of the given
formula?
Key points
You define evaluation strategy as the relationship between the point in your
code where you define your expression and the point when the expression is
actually evaluated.
With applicative-order evaluation, a function is executed only when all the
expressions you pass as input parameters are evaluated, starting from the
leftmost parameter.
A lambda expression is an anonymous function you can use as a value.
109
Chapter 4: Expression Evaluation,
Functional Programming in Kotlin by Tutorials Laziness & More About Functions
Type inference is the mechanism a compiler uses to understand the
element types involved in an expression.
A lambda expression is contravariant for the input type and covariant for
the output type.
Memoization is the process that allows you to save the result of a function in
memory to reuse it more efficiently.
Pure functions can always be memoized.
110
Functional Programming in Kotlin by Tutorials
5 Higher-Order Functions
Written by Massimo Carli
You’ll learn all this while using the Kotlin language and solving some fun
exercises. Open up the starter project for this chapter in IntelliJ and keep
reading.
But what does that mean? An example will make things easier.
Consider the following list of email address candidates that you’ll find in
Imperative.kt in this chapter’s material:
111
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
"12345@qqq.com",
"123.45",
"12345@a.b.c.d",
"fp_is_great@funprog.com",
"aaaaaaaaa.bbbbb@cccc.com",
"aaaaaaacccc.com",
"valid@jjjj.lll",
)
A first approach is the imperative one. In the same Imperative.kt file, write the
following code:
1. Define imperative as a function that returns the first five valid email
addresses longer than 10 characters.
fun main() {
println(imperative(emails))
}
112
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
Note: For an even more imperative approach, you can replace the enhanced
for loop:
With:
The declarative approach is closer to how humans think. Now, add the following
code to Declarative.kt:
113
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
fun main() {
println(declarative(emails))
}
When you look at the code, you realize it’s just a description of the steps you
want to do, which follows the initial requirements. You take five email addresses
from the ones you filtered because they matched the email regular expression
and were longer than 10 characters. You might already see some of the
declarative approach’s advantages, but you can do even better than this.
Code readability
The declarative approach improves code readability. In the same Declarative.kt
file, add the following code:
This is so declarative that you can read the code as if it were in normal, spoken
English. You basically:
114
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
filter the valid emails
filter the emails that are long enough
take 5 of them
In the rest of the book, you’ll see many examples of this, and you’ll see how
functional programming and the declarative approach get along. :]
Higher-order functions
By now you should know what a higher-order function is — you’ve already
written some of them. The most important one you saw in Chapter 2, “Function
Fundamentals” is:
In this next section, you’ll see some more examples of higher-order functions,
which:
115
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
This function executes the lambda the provided number of times. In this code,
you define:
The range as 1..this , and you iterate over it using forEach to execute
fn that number of times.
You can test how times works by adding and running this:
fun main() {
10.times {
print(" $it")
}
}
1 2 3 4 5 6 7 8 9 10
You probably think that, with the previous code, you cheated a little bit. :]
forEach is another higher-order function Kotlin provides as an extension
function for Iterable<T> , like the filter you met earlier:
116
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
OK, assuming you don’t want to use an existing higher-order function, you can
replace the previous implementation with the following imperative-ish code:
1 2 3 4 5 6 7 8 9 10
Exercise 5.1: Kotlin provides you with first , which returns the first
element of Iterable<T> for which a predicate you provide in input evaluates
to true . Remember that Iterable<T> is the abstraction of all the collections
providing Iterator<T> implementation.
117
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
Give it a try and check the challenge project or Appendix E to see how you
did.
This function sorts an input IntArray using the bubble sort. Understanding
how it’s sorting is less important here than knowing that it’s sorting Int s.
Note, the swap function is already available in Util.kt.
fun main() {
val array = intArrayOf(10, 5, 2, 7, 8, 3)
bubbleSort(array)
array.printAll()
}
printAll is a function in Util.kt. Run this code, and you get what you expect:
2 3 5 7 8 10
It’s important to note that you can use the bubble sort to sort anything, not just
Int . What you need is just a way to understand when a value is greater than
another value of the same type. How, then, can you abstract bubbleSort so you
can use it with any array of values of type T ?
Note: The bubble sort definitely isn’t the most performant sorting algorithm.
It’s used here because it does not need a lot of code.
118
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
Note: If you want to learn more about algorithm and data structures in Kotlin,
Data Structures & Algorithms in Kotlin is the best choice.
In the same file, replace the previous code with the following:
2. Pass the strategy as a lambda function of type (T, T) -> Boolean , which
returns true if the first value is greater than the second.
3. Use the isLarger lambda expression to compare each pair of values in the
array.
4. Swap the values, if needed, using the swap overload you find in Util.kt.
fun main() {
val array = arrayOf(10, 5, 2, 7, 8, 3)
bubbleSort(array) { first, second ->
first > second
}
array.printAll()
}
119
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
2 3 5 7 8 10
Optionally, can you also provide a way to “redo” the most recent Command ?
Functional interfaces
In the previous chapters, you learned the concept of function type. If you
consider the previous strategy pattern example, you could’ve defined the type
for the predicate parameter using typealias , like this:
With (T, T) -> Boolean , you’re basically representing the type of all the
functions with two parameters of type T returning a Boolean . With the
typealias keyword, you just gave that type the name IsLarger<T> .
Kotlin allows you to do something similar with functional interfaces. You find
120
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
the same concept with the acronym SAM, which stands for single abstract
method. For instance, open FunctionalInterface.kt and write the following
definition:
2. Define isLarger as the only abstract function for the functional interface.
Using the previous definition, you can add the following code to the same file:
121
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
fun main() {
val array = arrayOf(10, 5, 2, 7, 8, 3)
bubbleSortFI(array) { first, second -> // HERE
first > second
}
array.printAll()
}
fun main() {
val array = arrayOf(10, 5, 2, 7, 8, 3)
val largerStrategy = IsLarger<Int> { first, second -> // HERE
first > second
}
bubbleSortFI(array, largerStrategy)
array.printAll()
}
So, what’s the difference between the definition of a function type using
typealias and a functional interface?
122
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
fun main() {
val isEven = { number: Int -> number % 2 == 0 } // 3
isEven.whoAmI() // 4
}
I'm a typealias
This is because the definition of whoAmI above would be exactly the same as:
Now, add the following code to the same file, replacing the existing main
function:
fun other() { // 3
println("I can have other methods")
}
}
123
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
fun <T> ISinglePredicate<T>.whoAmI() = // 4
println("I'm a functional interface")
fun main() {
val isEvenFI = ISinglePredicate<Int> { number -> number % 2 == 0
} // 5
isEvenFI.whoAmI() // 6
isEvenFI.other() // 7
}
3. Also define other , proving that a functional interface must have one and
only one abstract method, but it can also have other methods. A small note
about these methods later.
4. Create the same extension method, whoAmI , which prints the fact that this is
about a functional interface.
3. Allows you to define one and only one operation. The others can be methods
working as default method implementations for an interface.
4. Invokes the abstract method of the functional interface. To achieve this, you
need to state its name explicitly. With the function you define using
typealias , you just use () or invoke . With ISinglePredicate<T> , you
124
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
need to call accept .
Note: In theory, an interface doesn’t define any methods but just operations,
which are public and abstract by definition. When you read the method of the
interface, it means methods adding something more to the one the interface
defines through its operations. An example of this is:
interface Reader {
fun readChar(): Char?
fun readString(): String {
TODO("Call readChar() until it returns null")
}
}
Exercise 5.3: Can you implement Reader in the previous note using a
function interface? How would you test it?
The answer is very simple, and it can be summarized with the concepts you find
in the SOLID principles. You won’t revisit all of them here because it’s not the
main topic, but just think about the meaning of the I there, which stands for
the interface segregation principle.
This basically states that no clients should be forced to depend on methods they don’t
use. Functional interfaces, or lambda expressions, define a single operation and
make it impossible for clients to depend on the lambda expressions if they don’t
use those functions.
At this point, you might argue that using just SAM would make it difficult to
implement interfaces like Reader . That’s not the case at all — and you’ll prove
it. :] Open Solid.kt and add the following code to define CharReader as the
interface with the sole responsibility of providing a single Char at a time.
125
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
If you need to read a String , you can use the most fundamental principle of
functional programming: composition. In the same file, add the following code:
With this, you can implement StringReader by adding some code that’s
basically the same as what you probably implemented in Exercise 5.3:
fun main() {
println(stringReader.readString("This is
String!".toCharReader()))
}
This is String!
126
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
3. Print String .
This function:
1. Has an input parameter of type Int and returns a function of type () ->
Int .
3. Returns a function that returns count and then increments its value.
fun main() {
val countFromFive = countFrom(5)
println(countFromFive())
println(countFromFive())
println(countFromFive())
}
5
6
7
As you see, countFrom initializes count , which is then captured by the lambda
it returns. All the variables outside the scope of a lambda function that the
127
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
How would the same function be different if you use the following functional
interface?
As a simple example here, you can keep playing with Predicate . Open
Predicates.kt and write the following code:
Note: In this chapter, to make different examples coexist in the same project,
sometimes you need to use a different name for the same concept. That’s why
you append 1 to Predicate here. Of course, you wouldn’t do the same in
your project.
128
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
You already know how to do this. In the same Predicates.kt file, add the
following code:
2. Return another Predicate1<T> , which evaluates the first one and, if true,
other .
You can use the same approach used in Exercise 5.4 with the following code:
fun main() {
val predicate = 4.isEqualsPredicate1() or 5.isEqualsPredicate1()
// 3
listOf(1, 2, 3, 4, 4, 5, 6, 7, 8, 8)
.filterWithPredicate(predicate) // 4
.forEach(::println)
}
129
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
3. Use the or you created earlier to create Predicate1<T> for filtering values
4 and 5 .
4
4
5
Exercise 5.5: Can you implement the same logic for implementing the
example in the Imperative vs. declarative approach section using the
definitions of Predicate1<T> and filterWithPredicate ? Given a list of email
addresses, you need to:
fun main() {
executor { println("Hello World!") }
}
130
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
1. Define executor , which just executes the lambda you pass as an input
parameter.
In this case, you don’t need to run this code, but repeat the same process you
followed in Chapter 3, “Functional Programming Concepts” for the Kotlin
decompiled code. When you do so, you end up with something like this:
As you see:
This is what you’d normally expect. Now, just add the inline keyword to
executor like this:
The first thing to note is that the IntelliJ warning you got in Chapter 3 doesn’t
show up.
Check the decompiled code, and you see that main becomes:
131
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
You can see how main no longer has an executor invocation. There’s not
even the invocation of the fn you pass as a parameter. The Kotlin compiler has
copied the content of fn exactly where the previous executor invocation
was.
The inlined executor is very simple, but if it contained more code, the
resultant bytecode would also need much more space. On the other hand, you
could return to the warning situation using the noinline keyword like this:
In this case, the Kotlin compiler would complain again, like this:
This is because the decompiled code would be like the initial one.
Non-local returns
Inlining can be useful for another reason, though. Update the code in Inline.kt
like the following:
fun main() {
executor {
var count = 0
while (true) {
count++
if (count > 5) {
return // ERROR
}
}
}
println("The End!")
}
132
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
If you don’t inline executor , the Kotlin compiler will complain because it
doesn’t know from what context it should actually return .
1. Use the label return@executor specifying that you want to exit from
executor .
2. Inline executor .
fun main() {
executor {
var count = 0
while (true) {
count++
if (count > 5) {
return@executor
}
}
}
println("The End!")
}
In the second case, you can just inline executor like this:
You must be very careful, though, because the behavior of your program differs
in the two cases.
133
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
When you use return@executor , you’re saying that when you reach return ,
you’re exiting from the body of executor . Run main , and you get:
The End!
Figure 5.4: Can't inline 'fn' here; it may contain non-local returns
The reason is that the compiler doesn’t know how to handle fn non-local
returns and suggests a solution of introducing the crossinline keyword to the
parameter like this:
134
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
This informs the Kotlin compiler that the lambda you pass as an input
parameter of executorExecutor can’t contain any non-local return. If it does,
you get an error:
If you really want to allow return in fn , you can simply use inline for both
executor and executorExecutor :
Challenges
In this chapter, you learned a lot about higher-order functions that are basically
functions of functions. You’ve already done some interesting exercises, but now
it’s time for a few more challenges.
135
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
items of type B .
Can you implement the function map for the Array<A> type with the following
signature?
fun main() {
val square = { a: Int -> a * a }
val toString = { a: Int -> "This is $a" }
arrayOf(1, 2, 3)
.map(square)
.forEach(::println)
arrayOf(1, 2, 3)
.map(toString)
.forEach(::println)
}
1
4
9
This is 1
This is 2
This is 3
Then, use it to return all the positive prime values in Array<Int> . A number is
prime if it’s not evenly divisible with any number other than 1 and itself.
Key points
A lambda expression is a function you can use as a value.
A higher-order function accepts other functions as input parameters or
provides other functions as return values.
136
Functional Programming in Kotlin by Tutorials Chapter 5: Higher-Order Functions
Kotlin allows you to define functional interfaces, which are a handy way to
define interfaces with a single abstract method that can be used as lambda
expressions.
Functional interfaces are also called SAM, which stands for single abstract
method.
A closure of a function is the set of all the variables the function can access,
which have been defined outside its own body.
A lambda function can modify its non-constant closures.
Always consider using the inline keyword for higher-order functions, but
double-check that the behavior is what you expect, especially in the context
of non-local returns.
The noinline and crossinline keywords are two additional options you
have to control the flow of your program when using higher-order functions.
137
Functional Programming in Kotlin by Tutorials
What read-only collections are and how Kotlin uses them to simulate
immutability.
You’ll learn all this by writing code in Kotlin and having some fun with
interesting exercises and challenges. Open up the chapter project to get started.
Immutability
Immutability is a relatively simple concept to understand: It basically describes
the process of writing your code using immutable objects. An immutable object
is an object whose state can’t be modified after it’s created. You describe
immutable objects using immutable classes.
138
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
In Chapter 2, “Function Fundamentals”, you learned that the state of an object
is the set of values of all its properties. Because of this, immutable classes
describing immutable object instances don’t define mutators or expose any
mutable internal state.
Before diving into the study and implementation of functional data structures in
Chapter 7, it’s crucial to describe what immutability is and why it’s important.
Open Immutable.kt in this chapter’s material, and write the following code:
User is an immutable class because you define each property using val and,
as you’ll see later, because String and Int are also immutable.
Looking at the decompiled code, you get something like the following:
@NotNull
public final String getUsername() { // 2
return this.username;
}
139
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Intrinsics.checkNotNullParameter(username, "username");
super();
this.id = id;
this.username = username;
}
// ...
}
Note: You learned how to get the decompiled code in IntelliJ in Chapter 3,
“Functional Programming Concepts” in the section “Referentially transparent
expressions and Kotlin inline functions”.
2. You have getters for each property exposing the related local private
instance variable. In this case, you see how the getters are also final to
prevent overriding them in a User subclass. This last point is redundant
because Kotlin classes can’t be extended by default, as you see in final
class User . It’s also worth noting that there are no setters for the two
properties.
Note: If you want to remove final from User in the generated code, you
need to declare User as a normal open or abstract class in Kotlin.
Remember that data classes can’t be open or abstract .
Here, you define all the properties using val , and the decompiled code
translates them in private and final instance variables with only getters.
140
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
fun main() {
val w = WrongImmutableUser(1, "maxcarli") // 1
println(w) // 2
w.dob.time = 1000L // 3
println(w) // 4
}
Here, you:
Running the previous code, you’ll see how the state has actually changed:
Note: The actual date you see in your output may differ. The Date default
constructor uses the current time.
141
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
2. Declare dob as a read-only property of the same type Date .
3. Create a copy of _dob ’s value every time the same property is accessed.
If you now run the same main you added earlier, you get the following output:
You tried to change the value of dob , but the state of WrongImmutableUser
didn’t change. This is because you acted on a copy of Date . This makes the
original WrongImmutableUser immutable, but the code isn’t the best, and you
also need to document this behavior somewhere.
1. The class MutableUser has a name giving information about the mutability.
As you’ll see later, the same happens with Kotlin collections where
MutableList<T> is the mutable version of List<T> .
Look at the decompiled code, and you now get something like this:
@NotNull
public final String getUsername() {
return this.username;
}
142
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Now, you can run the following code without any compilation errors:
fun main() {
val mutableUser = MutableUser(8, "maxcarli")
println(mutableUser)
mutableUser.username = "massimo"
println(mutableUser)
}
MutableUser(id=8, username=maxcarli)
MutableUser(id=8, username=massimo)
Immutability refers to objects whose state can’t change after they’re created.
fun main() {
val constantUser = MutableUser(1, "Max") // 1
constantUser = MutableUser(2, "Alice") // 2 ERROR
constantUser.username = "Alice"// 3
}
143
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
The same happens when you use a parameter of a function, like in the following
code:
144
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
to MutableList<E> .
fun main() {
val immutableList = listOf(1, 2, 3, 4, 5) // 1
val asMutableList = immutableList as MutableList<Int> // 2
asMutableList.add(10) // 3
// immutableList.add(10) // DOESN'T COMPILE
}
145
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
You get the immutability of the object provided by listOf by hiding add
behind the List<E> interface and making the object safe by throwing an
exception in the add implementation. The same is true for other mutators like
remove . For this reason, collections like List<T> are called read-only
collections.
Immutability advantages
So far, you’ve learned how to implement immutability in Kotlin, but you haven’t
actually seen why you should pursue immutability. The main reasons are:
Simplicity
Consistency
Avoiding duplication
Thread safety
Simplicity
Immutable classes are usually easier to implement and, for sure, easier to think
about. The reason for this is in the definition itself of an immutable object. If an
object can’t change its state after it’s been created, it means you don’t have to
implement mutators and logic to accept only values that are valid for the
specific domain.
146
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Consistency
Immutable objects are very good keys in a Map when mutable ones are
dangerous. To prove that, add the following code to Consistency.kt:
fun main() {
val key1 = MutableKey(1) // 2
val key2 = MutableKey(2) // 2
val myMap = mutableMapOf( // 3
key1 to "First",
key2 to "Second"
)
println("Value for $key1 is ${myMap[key1]}") // 4
key1.id = 2 // 5
println("Value for $key1 is ${myMap[key1]} after key1 update") //
6
println("Value for $key2 is ${myMap[key2]}") // 6
println("The Map is $myMap") // 7
myMap.remove(key1).also { println("Removed $key1 from myMap") }
// 8
myMap.remove(key2).also { println("Removed $key2 from myMap") }
// 8
println("The Map after remove is $myMap") // 8
println("Value for $key1 is ${myMap[key1]} after key1 remove") //
9
println("Value for $key2 is ${myMap[key2]} after key2 remove") //
9
}
147
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
4. Print the value for key1 . As expected, you get the value First .
6. Print the values for key1 and key2 , getting Second in both cases. This
isn’t obvious because when changing the value of id for key1 , you might
expect that the related value First has overridden the existing value
Second . This isn’t the case, but the situation is actually even worse.
7. Print myMap ’s current state, which contains two equal keys with two
different values. How can that be possible?
8. Try to remove the values for both key1 and key2 , and then print myMap ‘s
state again. You might expect an empty Map , but this isn’t the case. The
value for key1 is still there. Or, at least, that’s what you see in the output.
9. Access the values in the map for key1 and key2 , getting null for both.
Replace MutableKey with Key everywhere you’re using it. Using val for id
means the following line doesn’t compile:
// ...
key1.id = 2
// ...
Comment out key1.id = 2 so your code compiles. Run the previous code
using your new immutable keys, and you get the following output:
1. Contains both the keys for different id values and you can’t change that by
mutating the keys.
2. Is empty after removing the values for both key1 and key2 .
This is actually the proof that every class should be immutable unless there’s a
good reason to make it mutable. In this case, there isn’t a good reason to make
Key mutable.
Avoid duplication
In the previous example, you created two different instances of Key with two
different values for id . It’s interesting to see what happens if you create two
different instances of the immutable version of Key for the same id . Run the
following code after writing it in Reuse.kt:
fun main() {
val key1 = Key(1) // 1
val key2 = Key(1) // 1
val myMap = mutableMapOf<Key, String>()
myMap[key1] = "First" // 2
println("Value for $key1 is ${myMap[key1]}") // 3
println("The Map is $myMap") // 3
myMap[key2] = "Second" // 4
println("Value for $key2 is ${myMap[key2]}") // 5
println("The Map is $myMap") // 5
}
Here, you:
3. Print the value for key1 and the whole map, getting what you expect.
5. Print the value for key2 , checking that the new value has overridden the old
one.
149
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
The previous code proves key1 and key2 are effectively the same key, and
creating new instances of them is redundant. Because Key is immutable,
there’s no reason to create more instances for the same value of id . Reusing
the same object also simplifies the job of the compilers that can implement and
apply some low-level optimizations similar to the ones they apply to constants.
Exercise 6.1: In this section, you learned it’s useful to avoid creating multiple
instances of immutable classes because they represent the same value.
Given the following immutable class Id :
How would you change it to prevent any client from creating multiple
instances of Id for the same id ?
fun main() {
val id1 = // Create Id for id = 1
val id2 = // Create Id for id = 1
val id3 = // Create Id for id = 2
val id4 = // Create Id for id = 2
println("${id1 === id2}")
println("${id1 === id2}")
println("${id1 === id3}")
println("${id3 === id4}")
}
You get:
true
true
false
true
Exercise 6.2: What happens if the Id class in Exercise 6.1 is a data class?
150
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Thread safety
Immutable objects can be used safely by different threads without falling into an
inconsistent state. Concurrent programming is difficult, and you always need to
adopt synchronization strategies to avoid classic problems like race conditions
or deadlock. Using immutable objects helps, but it’s not always enough.
A race condition happens when multiple threads access shared data, and the
result depends on the order the operations happen. For instance, write this
code in Safety.kt:
val task = { // 3
randomDelay() // 4
counter.count++ // 3
randomDelay() // 4
if (counter.count == 2) { // 3
println("Completed")
}
}
fun main() { // 5
thread(block = task)
thread(block = task)
}
3. Define a lambda that increments the value of count and prints a message if
the counter value is 2 .
5. Finally, create main , where you start two different threads running the
same task on the same object counter .
Run main multiple times, and see how, sometimes, you get a single output like:
Completed
151
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Other times, you get:
Completed
Completed
Scenario 1: One thread accesses count and increments its value to 1 . The
same thread checks if the value is 2 , printing nothing. Now, the other
thread accesses count , increments it to 2 and executes the same test. The
test is now successful and prints the Completed message.
You might argue that this happens because of randomDelay . This is true, but
randomDelay simply simulates the scheduler’s behavior, which is the
component responsible for assigning each thread in the runnable state the
right to proceed. To be considered thread-safe, your code should work whatever
the scheduler algorithm is and hence, whatever order of instructions the
different threads run.
But how can immutability solve this specific problem? Well, immutability helps
you avoid the race condition because it prevents you from updating the state of a
shared object. To solve the counter problem, you need to approach it
differently. Here, you’re basically assuming that you want to:
152
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Run the task to increment the counter and check its value in a concurrent
way.
Print the Completed message only once if the value of counter is 2 .
In Chapter 17, “Sequence & Flow”, you’ll learn more about composition of
suspendable functions. But a possible naive solution could be the following,
which you can add in Safety.kt, making sure to comment out the existing
main :
fun main() {
val counter = Counter()
lateinit var counter1: Counter
val th1 = thread { // 3
counter1 = incAndCheck(counter)
}
th1.join() // 4
thread {
incAndCheck(counter1)
}
}
Note: In Chapter 8, “Composition”, you’ll see how to solve this problem using
the Kleisli category you learned in Chapter 3, “Functional Programming
Concepts”.
153
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
You introduced synchronization to make the code correct at the cost of a lower
throughput, which is the number of operations you can do in a period of time.
Note: In the case of more than two threads, the solution would be different,
much more complex, and is outside the scope of this book.
In conclusion, immutability helps you make your code more robust, but you
still need a way to use it properly in different situations.
fun main() {
val counter = MutableCounter() // 2
val th1 = thread { // 3
mutableIncAndCheck(counter)
}
th1.join() // 4
thread { // 5
mutableIncAndCheck(counter)
}
}
154
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Run the previous code multiple times, and you’ll always get Completed once.
This code is simpler because you don’t have to do all the copies and pass them
around.
Another option is creating a critical section, which is a block of code that only
one thread can access at a time. Open CriticalSection.kt and write the following
code:
@Synchronized // 1
fun syncedMutableIncAndCheck(counter: MutableCounter) {
randomDelay()
counter.count++
randomDelay()
if (counter.count == 2) {
println("Completed")
}
}
fun main() {
val counter = MutableCounter()
thread { // 2
syncedMutableIncAndCheck(counter)
}
thread { // 2
syncedMutableIncAndCheck(counter)
}
}
155
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Because only one thread can execute syncedMutableIncAndCheck at a time,
there’s no race condition on counter .
Immutability in JVM
At this point, a weird question might come to your mind. How can you change
immutable objects? The example in the previous section gives you an answer. If
you want to increment the count property of an immutable Counter object,
you just create a new instance, like this:
Is creating too many objects expensive? Of course, it is. Creating many objects
forces the garbage collector (GC) to start minor and major collections,
impacting the application’s performance. Fortunately, there are also ways to
reduce the impact:
fun main() {
val a: Int? = 1
val b: Int? = 1
println("Equals ${a == b}")
println("Same ${a === b}")
}
156
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Note: In this code, you’re using Int? , which Kotlin, on JVM, maps to
Integer . Compare this to Int , which Kotlin maps to the int primitive
type and doesn’t have the concept of reference. This is because Integer can
be null and int can’t. Because of this and the approaching introduction of
value classes, the === operator for referential equality on basic types has
been deprecated.
Equals true
Same true
Because a and b are of type Int? , the compiler maps them to an instance of
Integer . The instances should be different, and the referential equality should
return false , but this isn’t happening. The reason for this outcome isn’t
completely obvious. Replace the previous code with the following:
fun main() {
val a: Int? = 200
val b: Int? = 200
println("Equals ${a == b}")
println("Same ${a === b}")
}
Equals true
Same false
Hey, why is 1 === 1 true while 200 === 200 is false ? This is a design
choice done for performance reasons. When you assign the literal 200 to a
and b , the compiler automatically boxes the primitive value into an Integer .
If the value’s between -128 and 127 , the JVM is smart enough to recycle the
same values. If the literal is outside that interval, the JVM creates a new instance
every time. This is because of the assumption that small values are more likely
to be used multiple times in a program.
157
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Now you know all about immutability. You learned how to implement it in Kotlin
and what the trade-offs are in terms of thread safety and performance. But
what’s the relationship between immutability and functional programming? Is
immutability somehow related to the declarative approach you use when
writing higher-order functions? Is it actually possible to create applications
using only immutable objects? Is the immutability real, or is it just an illusion?
Pure functions
Higher-order functions
fun main() {
var total = 0 // 1
val list = listOf(1, 5, 10, 12, 34, 55, 80, 23, 35, 12, 80)
for (i in 0 until list.size) {
if (list[i] % 5 == 0) {
total += list[i] // 2
}
}
println("Total: $total")
}
This is imperative code that allows you to calculate the sum of the values in a
list that are multiples of 5 . It is not only imperative, but it contains a lot of
mutable variables in particular:
158
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
1. total for the result.
2. i as the index of the for to iterate over all the values in the list.
Total: 265
fun main() {
val multipleOf5 = { value: Int -> value % 5 == 0 }
val total = listOf(1, 5, 10, 12, 34, 55, 80, 23, 35, 12, 80)
.filter(multipleOf5)
.sum()
println("Total: $total")
}
When you run this code, you get the same result:
Total: 265
In this case, you don’t have anything mutable. You don’t see any index to update
for iterating over the list and no total to update with the elements that are
multiples of 5 in the list. It’s crucial to understand that this is what you see, but
it doesn’t mean it’s actually what’s happening. Control-Click on sum , and
IntelliJ gets you to the following code:
@kotlin.jvm.JvmName("sumOfInt")
public fun Iterable<Int>.sum(): Int {
var sum: Int = 0
for (element in this) {
sum += element
}
return sum
}
159
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
public inline fun <T, C : MutableCollection<in T>>
Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C
{
for (element in this) if (predicate(element))
destination.add(element)
return destination
}
This means that when you run the declarative code on List<T> with filter
and sum , you’re actually running code with all the mutations you had in your
first imperative implementation. The difference is that mutation is hidden in a
well-tested and safe place, which is perfectly fine.
The declarative approach you have with functional programming favors the use
of immutable objects, but it doesn’t completely prevent you from using mutable
objects instead.
In fact, nothing prevents you from writing the following ugly implementation of
the sum use case:
// DON'T DO THIS!!!
var total = 0
listOf(1, 5, 10, 12, 34, 55, 80, 23, 35, 12, 80)
.forEach {
if (it % 5 == 0) {
total += it
}
}
println("Total: $total")
Although the output is correct, here, you’re using the declarative function
forEach to change the state of the external variable total . Please, don’t do
that!
160
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
}
fun main() {
val list = listOf(1, 5, 10, 12, 34, 55, 80, 23, 35, 12, 80)
println(recAddMulti5(list))
}
Run the code, and you get the same value you got in the previous examples:
265
As you can see, there’s no apparent mutation here. This is because, during the
execution, no variables are changing their values, but you implement a similar
behavior, passing parameters to a recursive function. This might look expensive
and prone to stack-overflow errors, but not all recursive functions are equal.
Some recursive functions can be implemented using a loop with an evident
performance improvement. It’s the case of tail-recursive functions.
161
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Each move consists of taking the top disk from one of the stacks and
placing it on top of another stack or on an empty rod.
Tailrec functions
In the previous paragraph, you learned how recursion helps you accomplish
some tasks without any sort of mutation. Instead of changing the value of a
variable in a cycle, you invoke a function, passing the new value as parameter.
fun main() {
println(imperativeFactorial(10))
}
1. Initialize result to 1 .
162
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
3628800
Note: You represent the factorial of a positive integer value, n , with n!,
which is the result of multiplying all the integer values between 1 and n .
For instance, 3! = 3 * 2 * 1 = 6 .
So far, so good. In the previous paragraph, you learned that you can remove
mutation with recursion. Now, add the following code to the same file:
Run this code, and the output is, of course, the same:
fun main() {
println(recursiveFactorial(10))
}
3628800
The code is now shorter and more intuitive because it’s closer to the definition
of factorial. Take a closer look, and note how you always get the result by
multiplying the value of n by recursiveFactorial(n - 1) . The following is a
representation of what’s actually happening when you invoke
recursiveFactorial(5) :
recursiveFactorial(5)
5 * recursiveFactorial(4)
5 * 4 * recursiveFactorial(3)
5 * 4 * 3 * recursiveFactorial(2)
5 * 4 * 3 * 2 * recursiveFactorial(1)
5 * 4 * 3 * 2 * 1
5 * 4 * 3 * 2
5 * 4 * 6
5 * 24
120
163
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Can you do better? In this case, yes! Add the following code to the same file:
1. Define tailRecFactorial with two input parameters. The first is the value
n that you want to calculate the factorial of. The second is the current
result, fact .
Run this code, and the output is, of course, the same:
fun main() {
println(tailRecFactorial(10))
}
3628800
This time, repeat the previous exercise for recursiveFactorial(5) , and you
get the following sequence:
recursiveFactorial(5, 1) // 1
recursiveFactorial(4, 5) // 1 * 5
recursiveFactorial(3, 20) // 1 * 5 * 4
recursiveFactorial(2, 60) // 1 * 5 * 4 * 3
recursiveFactorial(1, 120) // 1 * 5 * 4 * 3 * 2
120
164
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
very important because it means that you could convert the recursion in a
simple loop. This is an example of tail recursion. Tail recursion is a particular
case of recursion, where the return value of a function is calculated as a call to
itself and nothing else.
Look at the decompiled code, and you’ll see something like the following:
Kotlin isn’t smart enough to understand that a function is tail recursive, so you
have to give it an hint. Just add the tailrec keyword like this:
tailrec fun tailRecFactorial(n: Int, fact: Int = 1): Int = when (n)
{ // HERE
1 -> fact
else -> tailRecFactorial(n - 1, n * fact)
}
The Kotlin compiler has replaced the recursion with a simple while loop,
which has an important impact on performance.
Mastering recursion is an important skill when you deal with functional data
structures, as you’ll see next in Chapter 7, “Functional Data Structures”.
165
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Can you prove this using the chrono function in Util.kt?
Challenges
In this chapter, you learned a lot about immutability and recursion. You’ve
already done some interesting exercises, but now it’s time for a couple more
challenges.
Key points
Immutable objects are objects whose state can’t be modified after they’re
created.
Immutable classes describe immutable objects. They’re usually simpler
because they don’t provide mutators.
Classes should be immutable unless there’s a very good reason to make them
mutable.
166
Functional Programming in Kotlin by Tutorials Chapter 6: Immutability & Recursion
Recursion is an important technique that allows you to handle mutation in a
safe way.
167
Functional Programming in Kotlin by Tutorials
Immutability and recursion are fundamental skills you need to understand and
implement immutable data structures and, in particular, persistence
collections. In this chapter, you’ll learn:
What pattern matching is and what you can actually achieve in Kotlin.
What the main functions for a collection are and how to implement them in
Kotlin.
As always, you’ll learn all this by solving some interesting exercises and
challenges.
As the name says, an immutable data structure is a data structure that can’t
change after it’s created. However, you can still add or remove values in a sense.
What you get from an immutable data structure after adding or removing an
168
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
element is another data structure with the element added or removed. This
might look like a waste of memory on the Java Virtual Machine with
performance consequences because of the garbage collector. This isn’t always
true.
Imagine you have to build a singly linked list which is, of course, initially empty
like in Figure 7.1:
You usually represent an empty list with the Nil value, but you could call it
Zero , Empty or even Null . It’s important to note how the empty list Nil
doesn’t depend on the type of values the list should contain. All the empty lists
are the same.
You can then try to add an element, for instance, an Int , getting what’s shown
in Figure 7.2:
169
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
oriented approach.
fun main() {
val emptyList: Node<*>? = null // 2
val singleValueList = Node(1) // 3
}
1. Node<T> as an immutable class with a property for value and one for the
optional next element in the list of type Node<T>? .
This is different from what you have in Figure 7.2 because there’s no explicit
relation between what you have in singleValueList and emptyList . Also,
emptyList is just a null value that doesn’t give meaning to the empty list
object.
Of course, you can make the relation with emptyList explicit, modifying the
previous code like this:
fun main() {
val emptyList: Node<*>? = null
val singleValueList = Node(1, emptyList as Node<Int>) // HERE
}
170
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
The following change would fix this warning, but again, it would just be another
way of creating a simple Node<T> , and you’d lose the relation with emptyList :
fun main() {
val singleValueList = Node(1, null)
}
To better understand how to implement the persistent singly linked list, look at
Figure 7.4, illustrating the list you get by adding a second element:
Again, you might still have the references to the previous emptyList and
singleValueList , but now you can find a pattern in how you build the list. In
this case, the object-oriented code gives you a hint. Just add the following
definition to the bottom of main :
This is quite normal code, but it gives you an idea; it helps you see every list as a
collection with the following characteristics:
1. It can be empty.
2. It can contain a value in the head with an optional list as the tail. These are
represented as value and next respectively within Node .
171
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
1. The sealed class FList<T> , which allows you to define a limited set of
implementations that Kotlin forces you to define in the same file or package.
2. Nil as an object that represents the empty list. Because the empty list is the
same for every type T , you can represent it as FList<Nothing> . This works
because Nothing is a subtype of every other type and because FList<T> is
covariant. You define the covariance of FList<T> using the out keyword. If
you need a reminder on covariance, take a peek at Chapter 4, “Expression
Evaluation, Laziness & More About Functions”.
Note: The name Cons comes from the word “Constructor”. For this reason,
one of the names for FList<T> is ConsList<T> .
FList<T> builders
In Kotlin, you can create different collection implementations using some
builder methods. For instance, you create a read-only list of Int with:
What builder function would you create for FList<T> ? Open Builders.kt and
write the following code:
This code allows you to create FList<T> using the following syntax:
fun main() {
// ...
val flist = fListOf(1, 2, 3)
}
172
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
2. The type for the vararg parameter is actually an Array , so in this case,
items has the type Array<T> . You then use sliceArray for getting
another array containing everything but the first element. If the initial array
is empty or contains just one element, tail will also be the empty
Array<T> .
3. If items is empty, you return Nil . Otherwise, you return FCons<T> where
head is the first element, and tail is the FList<T> you get, invoking
fListOf recursively on the sliced array.
Note: Note the use of the spread operator * , which allows you to use the
values in an array as if they were a list of multiple vararg input parameters.
fun main() {
// ...
val emptyList = fListOf() // ERROR
}
In this case, you have an error because you’re not providing the specific value
for the type parameter T . An easy fix would be to provide what’s missing, like
this:
fun main() {
// ...
val emptyList = fListOf<Int>()
}
But you know the empty list Nil is the same for every type, so the Int
information should be obsolete. In this case, you have two different options:
173
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
Add this code to main as an example:
fun main() {
val emptyList = Nil // 1
val singleElementFList = FCons(2, fListOf()) // 2
}
In this code:
companion object { // 1
@JvmStatic
fun <T> of(vararg items: T): FList<T> { // 2
val tail = items.sliceArray(1 until items.size)
return if (items.isEmpty()) {
empty()
} else {
FCons(items[0], of(*tail))
}
}
@JvmStatic
fun <T> empty(): FList<T> = Nil // 3
}
}
174
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
you to use FList.of() syntax. The body is very similar to the fListOf you
saw earlier. You replaced fListOf with of and Nil with the invocation of
empty .
3. Define empty as a builder for the empty list Nil . It’s important to see how
the return type is FList<T> . This simplifies the use of empty() in the
following examples.
fun main() {
val emptyList = FList.empty<Int>() // 1
val singleElementList = FList.of(1) // 2
val singleElementList2 = FCons(1, emptyList) // 3
val twoElementsList = FList.of(1, 2) // 4
}
4. Use FList.of with two Int values to properly create an FList<Int> with
two elements.
Declaring Nil and FCons<T> as internal has the advantage of hiding the actual
implementations in code in different modules and, as you’ll see very soon, this
might cause some problems. To understand what, it’s very useful to introduce
the concept of pattern matching.
Pattern matching
A simple exercise can help you understand what pattern matching is and how it
can be helpful. Suppose you want to implement size as a function that returns
the number of elements in a given FList<T> . Open Accessor.kt and write the
following code:
175
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
Because Nil and FCons<T> are internal , the previous code wouldn’t
compile if implemented in a different module. However, you should note a few
interesting things. Here, you:
1. Define the size extension function, which should return the number of
elements in FList<T> . The result value is the evaluation of a when
expression on this .
3. If the current FList<T> isn’t Nil , it means it has a head and tail .
size is then the size of the tail + 1 .
when(list){
Nil -> {}
(head, tail) -> {}
}
Unfortunately, that syntax doesn’t work yet with Kotlin, and it probably never
will.
Note: Kotlin provides very limited pattern matching. For instance, if you
release the constraint to have Nil and FCons<T> internal , you can make
the previous code for size compile and, for FCons<T> , the tail property
would be available as a consequence of the smart casting.
However, you can still do something to achieve a similar result. Open FList.kt
and add the following code:
176
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
2. Declare the first parameter whenNil as the lambda you want to evaluate if
the FList<T> receiver is Nil . The lambda whenNil evaluates in a value of
type S .
3. Define the second parameter, whenCons , as the lambda you want to evaluate
if the FList<T> receiver is FCons<T> . Again, the lambda whenCons
evaluates to a value of type S . Here, it’s important to note how whenCons
accepts head and tail as input parameters.
5. Use the smart casting Kotlin provides to extract head and tail if the
receiver value is FCons<T> and use them as input parameters for
whenCons .
Here, you implement size , returning the result of the match function
evaluating:
1. { 0 } if FList<T> is Nil .
To test the size function, just add the following code to the same file and run:
fun main() {
println(FList.empty<Int>().size())
println(FList.of(1).size())
println(FList.of(1, 2, 3).size())
}
177
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
0
1
3
Try to answer these questions without the support of IntelliJ and check your
solutions in Appendix G or the challenge project.
Note: The match function allows you to make the selection of the different
states more explicit. FList<T> can be Nil or FCons<T> . You’ll use it many
times in the rest of the chapter, but you could do the same directly using
Nil and FCons<T> and leveraging Kotlin’s smart cast. Remember, you can
use Nil and FCons<T> only in this module because of their internal
visibility.
Again, you can easily test this by adding the following code to main in the same
file:
fun main() {
// ...
println(FList.empty<Int>().head())
println(FList.of(1).head())
println(FList.of(1, 2, 3).head())
}
178
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
Run it, and check that you get the following output:
null
1
1
Exercise 7.2: Implement the extension function tail() , which returns the
tail of a given FList<T> .
Iteration
Iterating over a collection is one of the most important features a data structure
provides. How would you allow clients to iterate over the elements in
FList<T> ? The List<T> interface provides the forEach higher-order
function. Open Iteration.kt and add the following code:
fun main() {
listOf(1, 2, 3).forEach {
println(it)
}
}
1
2
3
To implement the same forEach for your FList<T> , add the following code to
the same file:
179
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
2. If the receiver is Nil , you do nothing.
3. If the receiver isn’t Nil , you invoke fn(head) and then recursively invoke
forEach on the tail .
fun main() {
// ...
FList.of(1, 2, 3).forEach {
println(it)
}
}
1
2
3
0 a
1 b
2 c
180
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
Mutators
You just implemented some interesting functions to access elements in
FList<T> or iterate over them. Now, it’s time to do something even more
interesting that will allow you to actually add or remove elements and update
the immutable singly linked list.
Inserting
In this chapter’s introduction, you saw, with some illustrations, how to add
elements at the head of FList<T> . Later, in Exercise 7.5, you’ll implement
addHead . Implementing append to add an element at the end of FList<T> is a
little more challenging because it implies copying the initial list to a new one.
Open Mutator.kt and add the following code:
fun main() {
val initialList = FList.of(1, 2)
val addedList = initialList.append(3)
initialList.forEach {
print("$it ")
}
println()
addedList.forEach {
print("$it ")
}
}
181
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
You’ll get:
1 2
1 2 3
Here:
3. Again, you invoke append(3) on the tail , which is Nil . This creates an
FList<Int> with the only element 3 .
Exercise 7.5: Implement addHead , which adds a new element at the head of
an existing FList<T> .
Filtering
In the previous chapters, you met the filter function that lets you select
elements using some predicate. How would you implement the filter
function for FList<T> ? In Filter.kt, add the following code:
Here, you:
182
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
fun main() {
FList.of(1, 2, 3, 4, 5, 6, 7, 8, 9)
.filter { it % 3 == 0 }
.forEach { println(it) }
}
This filters the values that are multiples of 3 in FList<Int> . In this case, the
output is:
3
6
9
Exercise 7.6: Kotlin defines the take function on Iterable<T> that allows
you to keep a given number of elements. For instance, running the following
code:
fun main() {
listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.take(3)
.forEach { print("$it ") }
You’d get:
1 2 3
183
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
allows you to keep a given number of elements at the end of the collection.
For instance, running the following code:
fun main() {
listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.takeLast(3)
.forEach { print("$it ") }
You’d get:
8 9 10
Immutable data structure: This data structure can’t change after it’s been
created. This means you can’t replace a value in a specific position with
another or remove another element. When performing this kind of
operation, you need to get another immutable data structure, as you’ve seen
for other immutable objects in Chapter 6, “Immutability & Recursion”.
Functional data structure: This is a data structure you can interact with
using only pure functions. For instance, you get a new FList<T> filtering
the data of another one using a predicate you represent using a pure
function. As you learned in Chapter 3, “Functional Programming Concepts”,
a pure function doesn’t have any side effects and is represented using a
referentially transparent expression. In the following chapters, you’ll see
many other functions like map , flatMap and others.
Persistent data structure: This data structure always preserves the previous
version of itself when it’s modified. They can be considered immutable, as
updates aren’t in place. The FList<T> you implemented in this chapter is
persistent. You see this when you add a new value. The existing object is still
there, and it just becomes the tail of the new one.
Challenges
In this chapter, you had a lot of fun implementing some of the classic functions
you find in collections for the singly linked list FList<T> . You also had the
chance to use the recursion skills you learned in Chapter 6, “Immutability &
184
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
fun main() {
println(FList.of(1,2,3,4,5).get(2))
}
You’d get:
Key points
An immutable data structure is a data structure that can’t change after it’s
been created.
A functional data structure is a data structure you can interact with using
only pure functions.
185
Functional Programming in Kotlin by Tutorials Chapter 7: Functional Data Structures
similar using the smart cast feature.
186
Functional Programming in Kotlin by Tutorials
8 Composition
Written by Massimo Carli
As usual, you’ll do this by writing Kotlin code with some interesting exercises
and challenges.
Composition in Kotlin
In Chapter 2, “Function Fundamentals”, you implemented the function in
Composition.kt:
187
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
To revise how they work, write and run the following code Composition.kt:
fun main() {
val double = { a: Int -> a * 2 } // 1
val square = { a: Int -> a * a } // 2
val stringify = Int::toString // 3
val stringifyDoubleSquareAfter =
stringify after square after double // 4
val stringifyDoubleSquareCompose =
double compose square compose stringify // 5
println(stringifyDoubleSquareAfter(2)) // 6
println(stringifyDoubleSquareCompose(2)) // 6
}
Here, you:
1. Define double as a function that doubles the Int passed in. This is a pure
function.
2. Define square as another pure function returning the square of the Int
passed as input.
3. Assign the reference of the toString function of Int to stringify .
16
16
The two functions return the same value, which isn’t as obvious of an outcome
as it seems. This works because, as you learned in Chapter 2, “Function
Fundamentals”, types and pure functions create a category and associativity is
one of the three properties.
188
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
fun main() {
val double = { a: Int -> a * 2 } // 1
val square = { a: Int -> a * a } // 1
val sum = { a: Int, b: Int -> a + b } // 2
val stringify = Int::toString // 3
}
In this case:
1. double and square are the same two pure functions for calculating the
double and the square of an Int value you saw earlier. The output type is
Int .
2. sum is a pure function with two input parameters of type Int . The return
value is of type Int as well, and it’s the sum of the input values.
3. stringify is the same function you met earlier that returns the String
representation of the Int input value.
So, how would you compose double and square with sum to return a
function that makes the sum of the double and the square of a couple of
Int values, as you see in Figure 8.1?
b
square
b*b
a*2+b*b
sum stringify
a*2
a
double
stringify(sum(double(10), square(2)))
To do this, you need to use a magic function: the curry function. You’ll prove
the curry function from a mathematical point of view in Chapter 10, “Algebraic
Data Types”. In this case, you’ll use it to understand why, so far, you’ve only
considered functions with a single input parameter. The truth is that single input
parameter functions are all you need. Every function with multiple parameters
can be represented as a higher-order function of a single parameter.
Note: The term “curry” comes from Haskell Curry, a renowned American
mathematician and logician. His first name, Haskell, is also the name of one
of the most important functional programming languages.
189
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
Before writing the generic curry function, how would you represent sum as a
function of a single parameter? In the same Curry.kt file, write the following
code:
Here, you define sum as a higher-order function with a single input parameter
that returns, as a result, another function of type (Int) -> Int . To
understand how this works, add the following code to main in Curry.kt and
run it:
Here, you:
1. Use sum as a function with a single input parameter of type Int , which
returns another function you save in addThree . In this case, addThree is a
function that adds 3 to the value you pass in.
You get:
190
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
parameter value to the initial value. Now it’s time to implement curry as a
generic function.
fun <A, B, C> Fun2<A, B, C>.curry(): (A) -> (B) -> C = { a: A -> //
1
{ b: B -> // 2
this(a, b) // 3
}
}
Here:
2. The return value of the function in 1 is another function that, this time, has
an input parameter b of type B .
3. Finally, the internal function has a body with the invocation of this , using
the parameters a and b .
fun main() {
// ...
val curriedSum = sum.curry() // 1 (Int) -> (Int) -> Int
val addThree = curriedSum(3) // 2 (Int) -> Int
val result = addThree(4) // 3 Int
println(result) // 4
}
Note: You should use the two-parameter version of sum you used earlier:
This code is very similar to what you implemented earlier. Here, you:
1. Use curry to get the curried version of sum . The type of curriedSum is
191
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
(Int) -> (Int) -> Int .
The sum example is pretty simple. But how can you solve the problem in Figure
8.1?
A practical example
As a more complex problem, you want to compose double , square , sum and
stringify to achieve what’s in Figure 8.1 and represent the following
expression:
stringify(sum(double(10), square(2)))
fun main() {
// ...
fun comp(a: Int, b: Int): String { // 1
val currySum: (Int) -> (Int) -> Int = sum.curry() // 2
val doubleComposeSum: (Int) -> (Int) -> Int =
double compose currySum // 3
val right: (Int) -> Int = doubleComposeSum(a) // 4
return (square compose right compose stringify)(b) // 5
}
192
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
2. Curry sum to create a single input parameter version.
3. Compose double with the curried version of sum . Remember, double has
type (Int) -> Int and sum.curry has type (Int) -> (Int) -> Int . This
means the composition has type (Int) -> (Int) -> Int .
4. Invoke the composition with input a . As a result, you get a function of type
(Int) -> Int in right . This function allows you to add an Int to a
defined value that, in this case, is the result of double(a) .
5. Invoke the result with the value of the input parameter b . Because now
right has type (Int) -> Int , you can easily compose it with square and
stringify .
To test the previous function, just add and run the following code in main :
println(comp(10, 2))
24
In the comp implementation, you might complain that it has too many
parentheses. Can you remove some of them? Of course, you can! In the same
Curry.kt file add the following code:
Note: Some developers don’t like using infix operators like pipe and
prefer using parentheses. It’s also sometimes difficult to find a name
everybody agrees on. Languages that allow you to create custom operators
usually represent the pipe with |> .
This simple function allows you to replace the previous comp implementation
with the following:
193
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
Kotlin doesn’t let you decide the precedence between compose and pipe , so
you still need some parentheses. But here, you can represent all of comp with
an expression on a single line.
That’s quite a loaded expression! Take a moment to break it down piece by piece
to help you understand it.
For more practice, try out the following exercises and use Appendix H to check
your solutions.
Exercise 8.1: Earlier, you implemented the generic curry function that
basically maps a function of type (A, B) -> C in a function of type (A) ->
(B) -> C . Can you now implement the uncurry function, which does the
inverse? It’s a function that maps a function of type (A) -> (B) -> C into a
function of type (A, B) -> C .
Exercise 8.3: The curry function maps a function of type Fun2<A, B, C>
into a function of type (A) -> (B) -> C . How would you define an overload of
curry for functions of three, four, five or, in general, n parameters?
Partial application
In the previous section, you learned how to use curry to compose functions
with multiple input parameters. This is very useful, but, as you saw in Exercise
8.3, it can also be cumbersome in the case of many parameters. Additionally,
most of the time, you don’t need to use just a single parameter at a time. To
understand this concept, write this code in Partial.kt:
194
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
fun interface DB { // 3
fun save(result: Double)
}
val calculatorFactoryImpl =
CalculatorFactory { db, logger -> // 5
object : Calculator {
override fun multiply(a: Double, b: Double): Double {
val result = a * b
db.save(result)
logger.log("$a * $b = $result")
return result
}
}
}
«interface»
CalculatorFactory
«interface»
«object» «depends»
DB
CalculatorFactorylmpl
+save (result:Double)
+ log(str: String)
195
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
Add the following code to the same file as an example of the use of
calculatorFactoryImpl :
fun main() {
val db = DB { // 1
println("Saving value: $it")
}
val simpleLogger = Logger { // 2
println("Logging: $it")
}
val fileLogger = Logger { // 3
println("Logging on File: $it")
}
val calculator1 =
calculatorFactoryImpl.create(db, simpleLogger) // 4
val calculator2 =
calculatorFactoryImpl.create(db, fileLogger) // 4
println(calculator1.multiply(2.0, 3.0)) // 5
println(calculator2.multiply(2.0, 3.0)) // 5
}
Here, you:
As you see, the log differs in the output for the Logger implementation.
196
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
parameters.
In the previous example, the value for the first parameter is the same for both
Calculator implementations. The second is different. The idea of partial
application is mapping the create function in CalculatorFactory in a
different function with a single input parameter that returns a function of the
second parameter, as you’ve seen in curry .
To understand how this works, replace the second part of main with the
following:
fun main() {
// ...
val partialFactory = calculatorFactoryImpl::create.curry() // 1
val partialFactoryWithDb = db pipe partialFactory // 2
val calculator1 = partialFactoryWithDb(simpleLogger) // 3
val calculator2 = partialFactoryWithDb(fileLogger) // 3
println(calculator1.multiply(2.0, 3.0)) // 4
println(calculator2.multiply(2.0, 3.0)) // 4
}
Here, you:
The previous example is very simple and starts from a function with just two
input parameters. Partial application is more powerful when the number of
input parameters is high. You noticed how the order of the parameters is
significant. You could play with flip and the overloads of curry , but this
would make the code very complicated. That’s why it’s important to keep partial
application in mind when you design your functions.
Note: You might have noticed some similarities between the previous
example and what happens with dependency injection. What you’ve seen is
197
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
an example of how to handle dependency injection in a functional way. In this
case, object-orientated and functional programming aren’t so different.
Partial application is basically what Dagger calls “assisted injection”.
Dependency injection is outside the scope of this book, but if you want to
learn all about it, Dagger by Tutorials is the right place for you.
Often, existing functions already follow this pattern, which is handy for you.
As a final tip on this topic, you should also consider that having functions with
too many parameters is generally bad practice.
This is a basic function that removes 1 from the square of the input. It doesn’t
really matter what this function does, but you know that:
198
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
1. x * x - 1 is a referentially transparent expression.
2. It has no side effects because you can invoke pureFunction(5) infinite
times, and you’ll always get the same result as output, as you can see when
you run the following code:
fun main() {
pureFunction(5) pipe ::println
pureFunction(5) pipe ::println
pureFunction(5) pipe ::println
}
Getting:
24
24
24
So far, so good. Now, it’s time to add a side effect with the following code you
write in the same file:
fun main() {
// ...
functionWithEffect(5) pipe ::println
functionWithEffect(5) pipe ::println
functionWithEffect(5) pipe ::println
}
199
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
Result: 24
24
Result: 24
24
Result: 24
24
It’s important to note, again, how the result value doesn’t tell you anything
about the side effect, which you can see only because of the console. You might
also think that it’s not so bad because it’s just a message in the standard output.
The problem is that this is just an example, and a side effect could be something
more important, like writing to a database or file or sending a request to a
server. Reading the function signature, you don’t have any information about
what the side effect is. More importantly, how would you test the
functionWithEffect function? This isn’t the only problem.
fun main() {
// ...
listOf(1, 2, 3) // 1
.map(::pureFunction) pipe ::println // 2, 3
}
Here, you:
[0, 3, 8]
200
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
This is the List<Int> you get when invoking pureFunction on each element
of the initial input. The map function has a very important property that says:
This says that invoking map with the function f first and then with the
function g is equivalent of invoking map on the composition of f and g .
This means you can prove this by running the following code:
fun main() {
listOf(1, 2, 3)
.map(::pureFunction).map(::pureFunction) pipe ::println
listOf(1, 2, 3)
.map(::pureFunction compose ::pureFunction) pipe ::println
}
And getting:
[-1, 8, 63]
[-1, 8, 63]
It’s interesting to test if the same is true for functionWithEffect . To see, you
just need to run the following code:
fun main() {
//...
listOf(1, 2,
3).map(::functionWithEffect).map(::functionWithEffect) pipe
::println
listOf(1, 2, 3).map(::functionWithEffect compose
::functionWithEffect) pipe ::println
}
Result: 0 // 1
Result: 3 // 2
Result: 8 // 3
Result: -1 // 4
Result: 8 // 5
Result: 63 // 6
[-1, 8, 63] // 7
Result: 0 // 1
Result: -1 // 2
Result: 3 // 3
Result: 8 // 4
Result: 8 // 5
Result: 63 // 6
[-1, 8, 63] // 7
201
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
This time, you can see how the output for rows 2, 3 and 4 are different. They’re
different because in the first example, all of the contents of the list are
transformed using the first invocation of functionWithEffect before working
through the second.
This means that the side effect you get from the composition isn’t the
composition of the side effect. This is proof that functions with side effects don’t
compose. How can you solve this problem, then?
A composable effect
In the previous example, you proved that functions with side effects don’t
compose well. One of the reasons is that composition means using the result of
a first function as the input of the second. If the side effect isn’t part of the
output, this makes composition difficult. What about removing the side effect
from the body of the function and passing the same information as part of the
value as output?
In the same SideEffects.kt file, add the following code you already met in part
in Chapter 3, “Functional Programming Concepts”:
2. The first Int property of the resulting Pair<Int, String> is the same
result as functionWithEffect .
Now, functionWithWriter doesn’t have any side effects, but the String you
202
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
want to print is part of the output. This makes functionWithWriter a pure
function. functionWithWriter doesn’t print anything, but it delegates the
responsibility of handling the side effect to the caller. But now you have a bigger
problem: functionWithWriter doesn’t compose with itself, and the following
code doesn’t compile:
// DOESN'T COMPILE
val compFunWithWriter =
::functionWithWriter compose ::functionWithWriter
This is because the compose function you created doesn’t match the signature
of functionWithWriter , which has an Int as input type and a Pair<Int,
String> as output. You know how to fix this, remembering that
compFunWithWriter is basically a Writer<Int> where:
Here, you:
4. Invoke g , passing the value of b you got at the previous instruction, de-
structuring the result in c and String str2 again.
203
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
// NOW COMPILES!
val compFunWithWriter =
::functionWithWriter compose ::functionWithWriter
Of course, you can compose multiple functions of type Writer<T> , as you can
see by running the following code:
fun main() {
val square = { a: Int -> a * a } // 1
val double = { a: Int -> a * 2 } // 1
val squareFunAndWrite = square compose ::functionWithWriter // 2
val doubleFunAndWrite = double compose ::functionWithWriter // 3
val compFunWithWriter = squareFunAndWrite compose
doubleFunAndWrite // 4
compFunWithWriter(5).second pipe ::println // 5
}
Here, you:
You’ll get:
Result: 624
Result: 1557503
204
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
typealias Writer<A, B> = (A) -> Pair<B, String>
How would you implement composition of the following type you define by
adding this to GeneralComposition.kt:
In Chapter 9, “Data Types”, you’ll learn much more about the optional type
along with many other fundamental data types. In this case, it’s interesting to
see how you’d compose functions of type Opt<A, B> .
You might think you can use the existing compose function because Opt<A,
B> is somehow included in the following, considering the B of Fun<A, B> as
the B? of Opt<A, B> :
fun main() {
val strToInt = { str: String ->
try {
str.toInt()
} catch (nfe: NumberFormatException) {
null
}
}
val findUser = { id: Int ->
if (id == 3) User(3, "Max") else null
}
val strToUser = strToInt compose findUser // DOESN'T COMPILE
}
The reason is that strToInt returns an optional Int? but findUser accepts
an Int . You can repeat the same pattern you learned for Writer<T> by adding
the following compose function:
205
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
3. Return a function with the input parameter a of type A and Opt<A, C> as
the output type.
4. Invoke the receiver function with the value of a . What you get is an optional
B? .
5. Check if b is null.
In the previous code, you understand how the logic in the body of the function
might be different, but it follows a common pattern you’ll see many more times
in the following chapters.
Note: Notice how similar this pattern is to using the Kotlin safe-call operator.
The difference is that your above compose is composing functions rather
than simply calling a function on an object.
fun main() {
val strToInt = { str: String ->
try {
str.toInt()
} catch (nfe: NumberFormatException) {
null
}
}
val findUser = { id: Int ->
if (id == 3) User(3, "Max") else null
}
val strToUser = strToInt compose findUser // 1
strToUser("a") pipe ::println // 2
strToUser("2") pipe ::println // 3
strToUser("3") pipe ::println // 4
}
Here, you:
206
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
3. Use strToUser for a missing user ID.
null
null
User(id=3, username=Max)
Where only the third case returns something that isn’t null .
Exercise 8.4: How would you apply the previous pattern for Array<T> ?
Basically, you need a way to compose functions of type:
Can you implement compose so that the following will compile and fun2 is
applied to all elements resulting from fun1 ?
Give it a try, and check your solution with the one in Appendix H.
Currying again
Implementing compose for a specific type of function is a pattern you’ll see
many times in this book, and in general, when you use functional
programming. In the previous example, you learned how to compose a function
with a particular side effect. The overloaded println function you used for
printing Int values is a function of type (Int) -> Unit . You also used the
overload of type (String) -> Unit . In any case, it’s a function with a String
input and Unit as output. Open CurryAgain.kt and write the following code:
This function isn’t pure because the expression it represents isn’t referentially
transparent. It depends on some external state that, in this case, you access
through the currentTimeMillis method of System . To prove that, just add
and run the following code:
fun main() {
functionWithAnotherEffect(5) pipe ::println
functionWithAnotherEffect(5) pipe ::println
}
Every time you invoke functionWithAnotherEffect with the same input, you
get different values as output. So, how would you make
functionWithAnotherEffect pure, and how would you handle composition?
In the println example, you had a function of type (Int) -> Unit and you
just moved the input for the effect to the output. Now, the function
System::currentTimeMillis has type () -> Long . A possible solution is
moving the value you get from System::currentTimeMillis to an input
parameter like this:
fun main() {
functionWithAnotherEffect(123L, 5) pipe ::println
functionWithAnotherEffect(123L, 5) pipe ::println
}
208
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
The first problem is easy to solve using Kotlin’s optional parameter. Just update
functionWithAnotherEffect like this:
fun functionWithAnotherEffect(
time: Long = System.currentTimeMillis(), x: Int
): String {
val result = x * x - 1
return "Result: $result calculated on $time"
}
Now, you can run this code where you used the explicit value as input for the
time parameter just when you want to test functionWithAnotherEffect .
fun main() {
functionWithAnotherEffect(x = 8) pipe ::println
functionWithAnotherEffect(123L, 5) pipe ::println
functionWithAnotherEffect(123L, 5) pipe ::println
}
What about composition, then? Well, that problem is solved with just a little bit
of curry!
209
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
To get the function to use during tests, you just need to use the following code:
fun main() {
// ...
val forTesting = 123L pipe ::functionWithAnotherEffect.curry() //
1
forTesting(5) pipe ::println // FOR TEST // 2
forTesting(5) pipe ::println // FOR TEST // 2
}
Here, you:
For ::functionWithAnotherEffect , you can reuse all the things you learned in
the section “Compose multi-parameter functions”.
Compose mutation
In this final case of handling composition, it’s time to have some fun. The goal is
to handle composition when the side effect of a function implies mutation. To
understand how this works, open Mutation.kt and add the following code:
210
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
You have the same problem you solved in the section “A common composition
pattern”, but now the effect is a mutation of a shared state you represent with an
instance of MutableCounter .
211
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
is the result of the function and second is the function you need to run on
MutableCounter to update its state.
Now, here’s the interesting part. How would you compose functions like this?
The types squareWithEffect and doubleWithEffect are similar to Writer<A,
B> , but in that case, it was defined as:
Now, the second element of the Pair<A, B> isn’t String , but Updater<S> .
This doesn’t change so much because you can apply what you learned in the
section “A common composition pattern”.
Using this, you can finally write the compose overload like this:
212
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
4. Invoke g , passing b as an input parameter. Using de-structuring, you get
the result you save in c of type C along with the Updater<S> you save in
op2 .
fun main() {
val composed = ::squareWithEffect compose
::doubleWithEffect compose ::squareWithEffect // 1
val counter = MutableCounter() // 2
val (result, compUpdate) = composed(3) // 3
result pipe ::println // 4
counter pipe compUpdate pipe ::println // 5
}
Here, you:
3. Use de-structuring to get the result in result and the composed mutation
in compUpdate .
4. Print the value of result .
324 // 1
MutableCounter(count=50) // 2
This is because:
Of course, the client is responsible for the execution of the effect. But, can you
do something better? Of course, you can. Do you really need a
MutableCounter ?
213
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
fun main() {
val composed = ::squareWithImmutableEffect compose
::doubleWithImmutableEffect compose
::squareWithImmutableEffect
val counter = Counter() // 4
val (result, compUpdate) = composed(3)
result pipe ::println
counter pipe compUpdate pipe ::println
}
This code has a few significant differences from the one in the previous section:
324
Counter(count=50)
This is the same result you got using MutableCounter , but this time you used
Counter , which is immutable.
214
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
Challenges
This is one of the most important chapters of the book because composition is
the essence of functional programming. You already did some interesting
exercises, so now it’s time for a couple challenges.
How would you implement compose for WithCallable<A, B> ? This is using
java.util.concurrent.Callable defined as:
interface Callable<V> {
@Throws(Exception::class)
fun call(): V
}
val three = { 3 } // 1
In this code:
They look like the same function, but they’re actually not. This is because you
need a Unit to invoke unitToThree . This also has consequences when you
compose. Consider the following code:
fun main() {
val double = { a: Int -> a * 2 } // 1
val comp2 = unitToThree compose double // 2 COMPILE
val comp1 = three compose double // 3 DOESN'T COMPILE
}
215
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
Here, you:
The reason is that you don’t have any compose overload with the type () -> T
as a receiver. The type (Unit) -> T instead falls into Fun<A, B> .
Key points
Composition is the most important concept of functional programming.
216
Functional Programming in Kotlin by Tutorials Chapter 8: Composition
This chapter completes the first section of the book. In the following section,
you’ll enter the core of functional programming, starting with the concept of
data types. See you there!
217
Functional Programming in Kotlin by Tutorials
9 Data Types
Written by Massimo Carli
In the first section of the book, you learned about the concept of type. In
particular, you learned that a type for a variable is a way to represent the set of
possible values you can assign to it. For instance, saying that a is an Int
means you can assign only integer values to a . The same is true for more
complex types like String or custom types like User and so on.
In this chapter, you’ll meet data types, another crucial concept that’s somewhat
orthogonal to the concept of type. For instance, the Optional<T> data type is a
classic example. It represents the concept of either having an object of type T
or not, and doesn’t depend on what T actually is.
What the fold and foldRight functions are and why they’re useful.
As always, you’ll learn all this using the Kotlin language and some fun exercises
and challenges.
var a: Int = 10
var s: String = "Hello World!"
var b = true
1. a is a variable of type Int . This means you can assign only integer values
218
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
to a .
2. s is of type String , and you can assign any possible String you can
create in Kotlin.
3. You can assign to the Boolean variable b only a value of either true or
false .
A type doesn’t just tell you what value you can assign but also what you can’t. In
the previous code, you can’t assign true to a , for instance.
You also learned that types and functions together make a category, which
is the pillar of composition.
A data type is a different concept that uses the previous types as type
parameters. In general, you represent them as M<T> in the case of a single type
parameter. Other data types have multiple parameters. For example, you
represent a data type with two type parameters as M<A, B> .
As you’ll see, you can think of a data type as a container that provides some
common functions so you can interact with its content. The best way to
understand data types is by providing some examples, starting with a classic:
Optional<T> .
companion object {
@JvmStatic
fun <T> lift(value: T): Optional<T> = Some(value) // 4
@JvmStatic
fun <T> empty(): Optional<T> = None // 5
}
}
219
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
In this code, you:
But how can you use the Optional<T> data type? A simple test can help.
Using Optional<T>
In OptionalTest.kt, add the following code:
Now, how would you implement code that doubles the value you get from
strToInt ? A first solution is the following:
220
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
fun main() {
val res = strToInt("10") // 1
when (res) {
is Some<Int> -> { // 2
val res2 = double(res.value)
println("Result is $res2")
}
is None -> println("Error!") // 3
}
}
2. Check the result and, if it’s a Some<Int> , you pass the value to double and
print the result.
Result is 20
To test the error, just pass a value to strToInt that isn’t a valid Int , like:
Error!
The previous code isn’t the best, though. You invoke strToInt and then use a
verbose when expression to understand what to do next. Of course, you can do
better.
221
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
2. Check if it’s a Some<Int> and get the Int in it.
The first option is the one you already implemented in the previous paragraph.
It’s time to implement the second, then. You call the first step lift because
you’re basically taking a value of type T and “lifting” it to an object of type
M<T> . In this case, M represents the Optional data type, but you’ll also find
the lift function in other data types.
flatMap map
Optional<String> Optional<Int> Optional<Int>
getOrValue()
lift()
String Int
In the same OptionalTest.kt file, replace the previous main with the following.
A keen eye might note that it won’t compile yet:
fun main() {
// ...
Optional
.lift("10")
.flatMap(::strToInt) // 1
.map(::double) // 2
.getOrDefault(-1) // 3
.pipe(::println)
}
Note: You’ll find the pipe definition you learned in Chapter 8, “Composition”
in Definitions.kt in the material for this project.
222
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
At the moment, this code doesn’t compile because you need to implement:
1. flatMap
2. map
3. getOrDefault
You’ll learn all about map and flatMap in Chapter 11, “Functors” and Chapter
13, “Understanding Monads”, respectively. At the moment, it’s important to
have an idea of how they work to make the previous code compile.
Implementing map
Starting with the map function, you see that it receives a function of type
Fun<A, B> as input and returns an Optional<B> . Remember that:
2. Check if the receiver is None . If it is, the result is also None . Note how you
use Optional.empty() , which allows you to return an Optional<B> by type
inference.
3. Use lift to return Some<B> , passing the result of the invocation of the
function fn .
Implementing atMap
While double is a Fun<Int, Int> , strToInt has type Fun<Int,
Optional<Int>> , making it incompatible with map . You need something more.
223
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
Add this code to Optional.kt:
2. You check if the receiver is None . In this case, you just return
Optional.empty() .
Note how even though fn already returns Optional<B> , you’re still wrapping
the result in a new Optional<B> instance. This is because every function
should return a new immutable object.
Great! One more function to go before you can compile your code.
Implementing getOrDefault
To ensure the previous code compiles, you also need to add getOrDefault to
Optional.kt:
224
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
Note how it accepts a value of type A .
Now, the previous code in OptionalTest.kt compiles. Run it, and you get:
20
To check the case with the default value, replace the previous main with the
following:
Optional
.lift("10aa")
.flatMap(::strToInt)
.map(::double)
.getOrDefault(-1)
.pipe(::println)
-1
A quick review
In the previous section, you met three of the most critical concepts in
functional programming. You’ll learn more about them in the following
chapters. In particular, you learned:
How to interact with the content of the container the data type represents
using map . You’ll learn all about functors in Chapter 11, “Functors”. For
now, it’s important to understand that invoking map on a data type M<A>
passing a function of type Fun<A, B> as a parameter, you’ll get M<B> .
How to interact with the content of a data type M<A> using a function of type
Fun<A, M<B>> . In this case, map doesn’t work. Instead, you need a function
called flatMap . You’ll learn all about flatMap in Chapter 13,
“Understanding Monads”. So far, you just need to understand that invoking
flatMap on a data type M<A> passing a function of type Fun<A, M<B>> as a
parameter, you’ll get M<B> .
Now, it’s time to learn the most common and important data types while
implementing for them lift , map , flatMap and the equivalent of
getOrDefault .
225
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
But first, here are some exercises to test your new knowledge! You can find
solutions in Appendix I and the challenge matterials for this chapter.
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.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 for it?
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.
fun main() {
val emptyList = emptyList<Int>() // 2
val intList = listOf(1, 2, 3) // 3
intList.map(::double).forEach(::println) // 4
println("---")
intList.flatMap(::countUpTo).forEach(::println) // 5
}
226
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
1. Defining countUpTo , which is a function of type Fun<Int, List<Int>> .
countUpTo just generates a List<Int> with values from 0 to the value
you pass in input. It doesn’t really matter what this function does; the type of
countUpTo is what matters.
2 // 1
4
6
---
0 // 2
0
1
0
1
2
1. map returns a new List<Int> that contains values that are the double of
the values of the original list.
Folding
List<T> has a couple of magic functions that are very important and useful in
the implementation of other functions. To see why, open Folding.kt and add the
following code:
227
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
Note: In Chapter 12, “Monoids & Semigroups”, you’ll learn even more about
the fold functions.
At this point, you’re probably disappointed because this function calculates the
sum of all the values in a List<Int> using an imperative approach. In Chapter
5, “Higher-Order Functions”, you learned how to use a declarative approach,
and in Chapter 6, “Immutability & Recursion”, you learned how to use recursion
to achieve immutability. In any case, the previous code teaches you that you
basically accumulate the different values of the list in a sum variable. You can
also use that in your tests to check if other implementations are correct. Run
this code:
fun main() {
val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
list.imperativeSum() pipe ::println
}
And you get the following output, which is the sum of the first ten positive
integers.
55
Note: If you want to have some fun, you can use the following expression as
an alternative way of printing the result of imperativeSum .
You already have pipe , epip and compose in the Composition.kt and
Definitions.kt files in the material for this chapter.
228
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
You’re basically doing the same as the imperative approach but using helper
as a tailrec function receiving as input the index pos of the current value in
the list and acc as the current sum. In this case, there’s no mutation, and the
approach is declarative. Test declarativeSum by running this code:
55
So far, so good. But this code works only for Int s. You can do much better. To
understand how, add the following code for a function that calculates the
product of the values in a List<Int> :
Run:
3628800
229
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
declarativeProduct , the initial value is 1 .
From the previous observations, you entail that you can represent
declarativeProduct and declarativeSum using a common abstraction
accepting as input something that tells where one differs from the other. In this
case:
2. Implement a helper function with two input parameters. The first is the
position pos of the current element you’re evaluating. The second is the
current value acc for the accumulator. If you reach the end of the list, you
return the current value for the accumulator, acc . Note how helper is a
tailrec function.
3. Call helper recursively for the next position, pos + 1 , if you’re not at the
end of List<T> . Note how the value for the accumulator is what you get by
invoking combineFunc with the current acc value and the current
element.
4. Invoke helper from the first position, 0 , and the initial value, start .
230
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
list.declarativeFold(0) { acc, item ->
acc + item
} pipe ::println
list.declarativeFold(1) { acc, item ->
acc * item
} pipe ::println
55
3628800
1. 0 as initial value { acc, item -> acc + item} as a combine function for
the sum.
2. 1 as initial value { acc, item -> acc * item} as a combine function for
the product.
As mentioned at the beginning of this section, you’ll see how powerful this
function is. Before proceeding, it’s also important to say that List<T> already
has a fold function with the same signature as declarativeFold , which you
created with a different name to avoid conflicts.
This means you can use the existing fold like in this code:
55
3628800
The Kotlin List<T> also provides a foldRight method, which differs in how
the combination happens. It’s very important to look at this function as well.
Folding right
Imagine you have a list of objects, and you want to group them together. To do
this, you have two different options. You can:
Start from the first element and accumulate the other objects as soon as you
iterate over them.
Start from the first element and, because you have others, put that object
231
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
aside and go to the next one. You repeat this operation until you get the last
object. Then, you start to take the most recent object you put aside and
combine it with the one you have in your hands. You repeat this operation
until you combine the object you put aside first.
Some code can probably help. Consider the following code you wrote earlier,
using a shorter list to save some space.
In this code, you use declarativeFold to calculate the sum of the first 5
integers you previously put into a List<Int> . It’s useful to see what happens
when you run this code:
(1,2,3,4,5).declarativeFold(0)
helper(0, 0) // 1
helper(1, combineFunc(0, 1)) // 2
helper(2, combineFunc(1, 2)) // 2
helper(3, combineFunc(3, 3)) // 2
helper(4, combineFunc(6, 4)) // 2
helper(5, combineFunc(10, 5)) // 2
15 // 3
15
15
15
15
15
1. Initially, you invoke helper(0, 0) because you start from the index, 0 ,
and the initial value is 0 .
2. When you’re not at the end of the List<Int> , you invoke helper again,
passing the new position, pos + 1 , as the next to evaluate and the new
value for the sum you’re accumulating. To get this, you need to invoke
combineFunc , passing the current value of acc and the current element in
the List<Int> . You do this until you reach the end of the List<Int> .
Because you’re returning the result of the same helper , this is a tailrec
function.
It’s also useful to see how the values in the list are actually aggregated:
combineFunc(combineFunc(combineFunc(combineFunc(combineFunc(0, 1),
2), 3), 4), 5)
232
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
(((((0 + 1) + 2) + 3) + 4) + 5)
Note how you’re accumulating values on the left, taking one new item at a time
from the right. This is why the declarativeFold you implemented is also
called foldLeft .
However, that’s not the only way to implement this. In the same Folding.kt file,
add this code:
In this case:
2. The helper function now has a single parameter: the position, pos , of the
current item.
3. When the recursion reaches the end of the list, you return the initial value,
start .
4. If you’re not at the end of the list, you return the result of the invocation of
combineFunc , passing the current item as the first parameter and the result
of the recursive invocation of helper for the following item.
233
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
(1,2,3,4,5).declarativeFoldRight(0)
helper(0)
comb(1, helper(2))
comb(1, comb(2, helper(3)))
comb(1, comb(2, comb(3, helper(4))))
comb(1, comb(2, comb(3, comb(4, helper(5)))))
comb(1, comb(2, comb(3, comb(4, comb(5, helper(6))))))
comb(1, comb(2, comb(3, comb(4, comb(5, 0)))))
comb(1, comb(2, comb(3, comb(4, 5))))
comb(1, comb(2, comb(3, 9)))
comb(1, comb(2, 12))
comb(1, 14)
15
15
(1 + (2 + (3 + (4 + (5 + 0)))))
2. You start combining from the last element and keep adding while returning
from the invocation stack.
55
3628800
Note: Notice that acc is the second param in the lambda you use as
combineFunc . This is for consistency with the Kotlin foldRight function.
234
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
} pipe ::println
list.map(Int::toString).fold("") { acc, item ->
acc + item
} pipe ::println
list.map(Int::toString).declarativeFoldRight("") { item, acc ->
acc + item
} pipe ::println
list.map(Int::toString).foldRight("") { item, acc ->
acc + item
} pipe ::println
And see that the results are different when using declarativeFold and
declarativeFoldRight or the existing Kotlin implementations:
12345678910
12345678910
10987654321
10987654321
Here, you can see that using declarativeFold or fold produces a different
result than declarativeFoldRight or foldRight .
Exercise 9.4: Implement a function that reverses a String using one of the
folding functions you’ve implemented in this chapter.
Note: Implementing all these functions for FList<T> is a great exercise. Feel
free to try it out on your own, skip this section or come back to it later if you
want and go straight to learning about the Either<A, B> data type. In any
case, see you there!
235
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
In this code:
fun main() {
val numbers = FList.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
numbers.fold(0) { acc, item -> acc + item } pipe ::println
numbers.fold(1) { acc, item -> acc * item } pipe ::println
}
55
3628800
236
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
fun <T, S> FList<T>.foldRight(
start: S,
combineFunc: (T, S) -> S
): S = when (this) {
is Nil -> start
is FCons<T> -> {
combineFunc(head, tail.foldRight(start, combineFunc))
}
}
In this code, note that foldRight isn’t tailrec anymore, similar to its
List<T> counterpart. This is because you return the result of combineFunc .
FList.of(
*("supercalifragilisticexpialidocious"
.toCharArray().toTypedArray())
)
.foldRight(StringBuilder()) { item, acc ->
acc.append(item)
acc
} pipe ::println
suoicodilaipxecitsiligarfilacrepus
Implementing map
map is one of the most crucial functions, and you’ll meet it many times when
implementing your code. Its implementation is very simple. In FListExt.kt, add
the following code:
This code follows most of the patterns you’ve seen in the previous examples.
Here:
3. If the current receiver isn’t Nil , it means it has a head of type T . In this
case, you return a new FList<S> where the head is the value of type S
237
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
you get from fn(head) and the tail is what you get by invoking map on it.
FList.of(1, 2, 3, 4, 5)
.map(::double)
.forEach(::println)
2
4
6
8
10
Implementing atMap
flatMap is probably the most challenging function to implement. It’s also part
of the proof that fold and foldRight should be fundamental elements of
your functional programming skills.
As the name says, this appends an FList<T> to another you have as the
receiver. This is another example of the use of the magic foldRight . Here, you:
Start with the FList<T> you want to append as the initial value.
Then, iterate over the elements in the receiver FList<T> , adding it as head
every time.
You get:
238
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
1
2
3
4
5
6
To test this code, run the equivalent example you met earlier with List<T> .
First, add this function:
0
0
1
0
1
2
239
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
companion object {
@JvmStatic
fun <A> left(left: A): Either<A, Nothing> = Left(left) // 4
@JvmStatic
fun <B> right(right: B): Either<Nothing, B> = Right(right) // 4
}
}
1. Either<A, B> as a sealed class in the type parameters A and B . Note how
Either<A, B> is covariant for both A and B .
4. The builders left and right , which return a Left<B> and a Right<A> ,
respectively, as objects of the abstract type Either<A, B> .
The use of a sealed class guarantees that an Either<A, B> can only be an
object Left<A> or Right<B> . But when would this be useful? As mentioned
earlier, a classic example deals with error handling. In this scenario, the name
of the possible values gives a hint. Right<A> is successful, and Left<B>
represents something wrong.
240
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
fun strToIntEither(
str: String
): Either<NumberFormatException, Int> = try {
Either.right(str.toInt())
} catch (nfe: NumberFormatException) {
Either.left(nfe)
}
In the previous chapters, you learned that you can make a function pure by
moving the side effect as part of the return value. This is what’s happening here.
The only difference now is that the return value is an
Either<NumberFormatException, Int> . In the case of success,
strToIntEither returns Right<Int> . In the case of failure, it returns
Left<NumberFormatException> .
The question now is: How do you interact with this value? The good news is that
you already know the answer. Either<A, B> is a container with an object of
type A or B in it. Every container should provide functions that allow you to
interact with the content. The most important functions are still map and
flatMap . Of course, their meaning is slightly different in the context of
Either<A, B> . You can start simple, with map .
Implementing map
The most important and — fortunately — the easiest functionality to implement
is map . But how can you provide a function of type Fun<A, B> if you don’t
even know if Either<A, B> is Left<A> or Right<B> ? The answer is very
simple: You provide two. Add the following code to Either.kt:
As you see, bimap accepts two functions as input parameters. The first, fl , is
the function of type Fun<A, C> — you apply this to the value of type A if
Either<A, B> is Left<A> . fr , however, is a function of type Fun<B,C> —
you apply this if Either<A, B> is Right<B> .
241
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
Note: In Chapter 11, “Functors”, you’ll learn that a data type providing a
function like bimap is a bifunctor.
Sometimes, you don’t want to provide two functions. For this reason,
Either<A, B> should also provide two different map functions.
To see how, just add the following code in the same Either.kt file:
In this case:
1. leftMap applies the function of type Fun<A, C> to the value in Left<A> .
Before showing an example using these, it’s helpful to see some accessor
methods.
Implementing accessors
If you think of every data type as a container, it’s often useful to define a
function to get their content, like the getOrDefault function you met earlier.
In this case, you can use different approaches. In Scala, for instance, the
Either<A, B> type provides a getOrDefault only for the Right<B> value.
If you decide to do the same, you can add the following code to the same
Either.kt file:
242
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
is Left<A> -> defaultValue
is Right<B> -> right
}
This function returns defaultValue if it’s Left<A> and the right value if it’s
Right<B> .
Nothing prevents you from implementing a specific function for Left<A> and
Right<B> , like these you can add to the same file:
Defining a flip function that swaps the two types, like this, is also interesting:
This allows you to use getOrDefault after flip to access the value for
Left<A> . A lot of fun!
These functions allow you to run an example of the use for bimap , mapLeft
and mapRight . Open EitherTest.kt and add the following code:
fun main() {
val squareValue = { a: Int -> a * a }
val formatError = { ex: Exception ->
"Error ${ex.localizedMessage}"
}
strToIntEither("10").bimap(formatError, squareValue) // 1
.getOrDefault(-1).pipe(::println)
strToIntEither("10").bimap(formatError, squareValue) // 2
.flip().getOrDefault("No Error!")
.pipe(::println)
strToIntEither("10").rightMap(squareValue) // 3
.getOrDefault(-1).pipe(::println)
strToIntEither("10aaa").leftMap(formatError) // 4
.getOrDefault("Generic Error").pipe(::println)
}
243
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
You can try different combinations, but here you have examples of using:
1. bimap passing formatError to format the error message in the case of the
Left<A> value, and squareValue to square the value in the case of
Right<B> .
2. bimap with the same formatError and squareValue functions, but using
flip to get the value in the case of Left<A> .
Implementing atMap
As mentioned earlier, Either<A, B> is usually right-biased. This means you
usually find functions like map and flatMap applicable to the Right<B> side
of it, which usually represents success. Left<A> usually represents failure, and
there’s not normally too much to do in this case. For this reason, you’ll
implement flatMap for the Right<B> side. In Either.kt, add the following
code:
1. Define flatMap as an extension function for Either<A, B> . Note how the
function fn you pass in as a parameter has type (B) -> Either<A, D> ,
which means the type for Left<A> doesn’t change. In Chapter 12, “Monoids
& Semigroups”, you’ll see much more about this. Finally, the return type is
Either<A, D> .
244
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
5. Finally, return a new Right<D> , using the value of the same type you get
from fn .
fun main() {
val squareValue = { a: Int -> a * a }
strToIntEither("10")
.rightMap(squareValue)
.rightMap(Int::toString)
.flatMap(::strToIntEither) // HERE
.getOrDefault(-1)
.pipe(::println)
}
100
Using Either<A, B> in a failure/success scenario is very common, and for this
reason, Kotlin provides the Result<T> data type, which you’ll learn about in
Chapter 14, “Error Handling With Functional Programming”.
Challenges
You’ve already done some interesting exercises dealing with data types. But
here’s an opportunity to have some more fun with a few challenges.
245
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
Key points
A type is basically a way to represent a set of values you can assign to a
variable or, in general, use in your program.
lift is the function you use to “elevate” a value of type T into a data type
of M<T> .
map allows you to interact with a value in a data type applying a function.
You’ll learn all about map in Chapter 11, “Functors”.
flatMap allows you to interact with a value in a data type M<T> using a
function that also returns an M<T> . You’ll learn all about flatMap in
Chapter 13, “Understanding Monads”.
List<T> is a data type that contains an ordered collection of values of type
T .
fold and foldRight are magical functions you can use to implement
many other functions.
The Either<A, B> data type allows you to represent a container that can
only contain a value of type A or a value of type B .
You usually use Either<A, B> in the context of success or failure in the
execution of a specific operation.
Either<A, B> has two type parameters. For this reason, it defines functions
like bimap , leftMap and rightMap that you apply explicitly on one of the
values.
Some data types with multiple parameters, like Either<A, B> , have
246
Functional Programming in Kotlin by Tutorials Chapter 9: Data Types
functions that are biased on one of them. For instance, Either<A, B> is
right-biased and provides functions that implicitly apply to its Right<B>
side.
247
Functional Programming in Kotlin by Tutorials
What algebra is and how it translates to the class construct and the
Either<E, T> data type in Kotlin.
How and when algebraic data types are useful, including a practical
example.
Understanding algebraic data types and their use will help you master
functional programming, as they’re especially useful for encoding business
logic in applications.
Time to do some coding magic with Kotlin and some interesting exercises!
Note: This is a very theoretical chapter that gives you some mathematical
proofs of the concepts you’ve met so far in the book. Feel free to skip it or
read it later if you want.
What is algebra?
Algebra is a category of arithmetic that lets you combine numbers with letters
representing numbers by using specific rules. Here’s an example of a simple
algebraic expression:
a * X ^ 2 - b * X + c
248
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
In this example, you have:
Algebra is the set of rules that allow you to combine all those different symbols.
But what does this have to do with Kotlin and functional programming?
This class consists of a simple pair of values, the first of type A and the second
of type B .
What about the Pair<A, B> type? How many values are available for a variable
of type Pair<A, B> ? To understand this, consider the type you’re defining by
copying the following code into Struct.kt, which you’ll find in this chapter’s
material:
To count all the possible values for a variable of type BoolPair , simply add the
following code:
249
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
From a pair of Boolean variables, which you can consider a value of 2 , you
get 4 values in total. But do you get those four values by adding 2 + 2 or
multiplying 2 * 2 ?
Answer this question by adding the following definition to the same file:
This defines the Triage type, which is an enum with three different values:
RED , YELLOW and GREEN . Next, add the following code:
This defines a Pair consisting of a Boolean and a Triage . Now, repeat the
same question: How many values does this type have?
Boolean * Triage = 2 * 3 = 6
This illustrates that a Pair<A, B> has as many values as the product of
multiplying A ’s values by B ’s values. This is called the Cartesian product of A
and B, which you can represent as A × B. This concept becomes much easier if
you use the analogy of a type with a set of values you learned in Chapter 2,
“Function Fundamentals”. If A represents all the values of type A and B all the
values of type B , the set A × B represents the product of the types A and B . A
× B is the set of all pairs (a, b) where a is an element of A and b an element
of B.
Now, look at what happens when incorporating the Unit type into the
multiplication.
250
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
Note: The cardinality of a set is the number of elements that set can
represent. For example, the cardinality of a Boolean would be 2, and the
cardinality of the Triage class above is 3.
Now, note that the number of possible values is the value you get by adding the
following code to the same file:
Unit * Triage = 1 * 3 = 3
This proves the Unit is equivalent to the value 1 when you multiply by it. It’s
also important to note that:
251
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
It looks like TriageUnit and UnitTriage are the same. They aren’t, but, in
terms of functions, they’re not so different. You can implement a function that
maps every element of TriageUnit to one and only one element of UnitTriage
and vice versa. This function is then isomorphic. This means a function of type
Fun<A, UnitTriage> isn’t so different from Fun<A, TriageUnit> . This is
because you can think of the latter as the composition of the former with an
isomorphic function.
This function has type Fun<Int, Boolean> and returns true if the Int value
in input is even. Consider, now, the following function:
In this case, you just use different values to represent the same information
about the Int passed as input. isEvenInt has type Fun<Int, Int> instead of
the type Fun<Int, Boolean> of isEven . The Int value’s information you get
from isEvenInt is actually the same as the Boolean you get from isEven .
From a mathematical point of view, you can think of them as the same thing and
say that TriageUnit and UnitTriage are isomorphic types. From a category
theory point of view, you can say that isomorphic types have the same
structures.
252
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
Exercise 10.2: What’s the cardinality of the following type?
When you try to add the following code, you get an error. This is because you
can’t have a value of type Nothing , so you can’t create an instance of the
NothingTriage type.
This means the type Nothing corresponds to the value 0 for multiplication
purposes. In this case, you can say that:
Using the set analogy, Nothing represents the empty set. You might wonder if
an empty Set<A> is different from an empty Set<B> . They’re definitely
isomorphic. You can then say that NothingTriage and Nothing are
isomorphic types, as it would be the type you define like:
Multiplying classes
In the previous examples, you used Pair<A, B> , but what happens if you
define a class like so:
253
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
The number of possible values is the product of multiplying all the values of the
aggregated types. In this example, Byte has 256 values, so the total number of
values is 1,536.
String has many possible values, so you can’t determine an exact result — but
having an exact result isn’t important. As you’ll see later, the important thing is
to understand that you can represent the relationship between types as a
multiplication operation.
This is the Either<E, A> data type, representing a value of type A or a value of
type B . For your next step, you’ll repeat the same exercise. But this time, you’ll
try to understand how many values the Either<A, B> type has in relation to
the number of values of A and B .
254
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
This is the list of all possible values of the EitherBooleanOrBoolean type, which
you can think of as:
Boolean + Boolean = 2 + 2 = 4
This, perhaps, isn’t the best example because, as you saw earlier, 4 is 2 + 2
but also 2 * 2 . However, you’ve already learned how to solve this problem.
Boolean + Triage = 2 + 3 = 5
The Boolean type has 2 values and the Triage type has 3 values, so the
EitherBooleanOrTriage type has 2 + 3 = 5 values in total.
255
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
Boolean + Nothing = 2 + 0 = 2
Boolean + Unit = 2 + 1 = 3
256
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
It’s important to note that you define the Result and Error types as optional.
You always receive some data back from the asynchronous function.
If the result is successful, you receive the content in a Result object, which
is null otherwise.
If there are any errors, you receive a value of type Error , which is also
null otherwise.
To simulate a typical use case of the previous type, enter the following code into
TypeSafeCallback.kt:
// 1
class Response
class Info
class ErrorInfo
// 2
fun runAsync(callback: Callback<Response, Info, ErrorInfo>) {
// TODO
}
1. Define some types to use as placeholders. You don’t really care about what’s
inside those classes here.
If there’s an error, you could use the following code to return the Response
257
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
along with ErrorInfo , which encapsulates information about the problem.
But there’s a problem with this: The type you define using the Callback<Data,
Result, Error> typealias isn’t type-safe. It describes values that make no
sense in runAsync ‘s case. That type doesn’t prevent you from having code like
the following:
This is because the return type allows those values. You need a way to
implement type safety.
258
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
The safe version of runAsync now looks like the following code, which you can
also add to TypeSafeCallback.kt:
The only values you can return using the safe callback are:
More important than what you can do is what you can’t do. You can’t return both
Info and ErrorInfo , but you must return at least one of them.
A * 1 = A = 1 * A
A * Unit = A = Unit * A
This tells you that Pair<A, Unit> is the same as Pair<Unit, A> , which is the
same as A , as you saw earlier in this chapter about isomorphism.
Another way to say this is that adding a property of type Unit to an existing
type doesn’t add any useful information.
259
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
A + 0 = A = 0 + A
Becomes:
A + Nothing = A = Nothing + A
Finally, write:
A * 0 = 0 = 0 * A
Which becomes:
A * Nothing = 0 = Nothing * A
You can’t create a Pair using a value of type A and Nothing , so this is
basically the Nothing type.
This means the Either<A, Unit> type has all the possible values of A plus a
single value that is Unit . This is something you could represent like this:
Do you recognize it? This is basically the Optional<T> type you learned about
in Chapter 9, “Data Types”. You have a value of type A , or you have another
260
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
single and unique value, which is None here, but could also be Unit .
// 1
A ^ 2 = A * A = Pair<A, A>
// 2
A ^ 3 = A * A * A = Pair<A, Pair<A, A>> = Pair<Pair<A, A>, A>
// ...
But what about the meaning of the expression A ^ B, where A and B are
types? How many possible values can you represent with a type that
corresponds with the expression, like Boolean ^ Triage ?
If the analogy between types and algebra is true, the number of values for the
type Boolean ^ Triage should be 8 because:
Boolean ^ Triage = 2 ^ 3 = 8
But what does the number 8 represent? It represents how you can take the
number of values of Boolean to the power of the number of values of Triage .
This can happen in multiple ways — which are the number of ways to associate a
Boolean value with a value of the Triage type.
This perfectly describes the (Triage) -> Boolean function type. Prove this by
adding the following code to Exponents.kt:
261
Functional Programming in Kotlin by Tutorials
fun func0(triage: Triage): Boolean = when
Chapter 10: Algebraic Data Types
(triage) {
Triage.RED -> false
Triage.YELLOW -> false
Triage.GREEN -> false
}
There are exactly 8 different ways of mapping a Triage value into a Boolean
value. Think of A ^ B as equivalent to a function from B to A . You can then
assert that:
A ^ B = (B) -> A
262
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
Proving currying
In Chapter 8, “Composition”, you learned about the curry function. It basically
allows you to define the equivalence between a function of type (A, B) -> C
with a function of type (A) -> (B) -> C . But where does curry come from?
Is it something that always works, or is it a fluke? It’s time to prove it.
In the previous section, you learned that exponential A ^ B can be translated
in a function from B to A of type (B) -> A or Fun<B, A> . One of the most
important properties of exponents is the following:
(A ^ B) ^ C = A ^ (B * C)
A ^ (B * C) = (A ^ B) ^ C
Sorting the types’ variables in an easier way, you can read that equation by
saying that a function of two input parameters, A and B with output C —
(A, B) -> C — is equivalent to a function with an input parameter of A and
an output parameter of function type (B) -> C . This is exactly what you’d call
currying. Here’s what you’ll find in Curry.kt in the material for this chapter:
fun <A, B, C> Fun2<A, B, C>.curry(): (A) -> (B) -> C = { a: A ->
{ b: B ->
this(a, b)
}
}
A ^ 0 = 1
263
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
A ^ Nothing = Unit
Which means:
In this case, the tricky thing is that = means isomorphism. So how can you read
the previous definition? In Chapter 2, “Function Fundamentals”, you saw a
function of type Fun<Nothing, A> , which you called the “absurd function”
because you can’t invoke it. To invoke that function, you need a value of type
Nothing , which doesn’t exist. Because you can never invoke that function, all
the functions of that type are the same. They all have the same meaning, and
they all produce — or better, never produce — the same result.
Another way to say that all those functions are the same is: If you take one of
those, all the others are equivalent. You can represent all of them with just one,
and a way to represent a singleton is with Unit .
Powers and 1
Keep having fun with the following equivalence:
1 ^ A = 1
Whatever the exponent is for 1 , you always get 1 . Using equivalence with
types, you can say that:
This means there’s only one function from a type A to Unit . It’s a way to
reinforce the definition of a terminal object that states there’s a unique
morphism from any object to it.
A ^ 1 = A
264
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
(Unit) -> A = A
This is another way to define the unit function you also learned about in
Chapter 2, “Function Fundamentals”.
A ^ (B + C) = A ^ B * A ^ C
This is a simple sealed class, which represents all natural numbers as:
// 1
val ZERO = Zero
// 2
val ONE = Successor(Zero)
// 3
val TWO = Successor(Successor(Zero)) // Successor(ONE)
// 4
val THREE = Successor(Successor(Successor(Zero))) // Successor(TWO)
// 5
// ...
265
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
5. And so on…
What’s more interesting is comparing the previous definition with the one of
Either<A, B> :
NaturalNumber = 1 + NaturalNumber
NaturalNumber = 1 + NaturalNumber
NaturalNumber = 1 + (1 + NaturalNumber)
NaturalNumber = 1 + (1 + (1 + NaturalNumber))
NaturalNumber = 1 + (1 + (1 + (1 + NaturalNumber)))
...
This suggests that the set of NaturalNumber can be seen as a sequence of ones,
one for each natural number. Now, consider the List<A> data type. Using the
same reasoning, think of it as something you can define by entering the
following code into List.kt:
Does it ring a bell? You used this in Chapter 7, “Functional Data Structures”.
This means that a FList<A> can be empty, or you can think of it as a head and
tail , which may or may not be empty. You can then create a list of five values
in the following way and add it to List.kt:
val countList =
FCons(1, FCons(2, FCons(3, FCons(4, FCons(5, Nil)))))
266
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
An immutable characteristic of math is that it always makes all the pieces work
together.
fun main() {
println(countList.sum())
}
15
So far, so good. But from an algebraic point of view, you write the previous
FList<A> type like this:
FList<A> = 1 + A * FList<A>
This is because it can be the Nil (and so the 1) or a Pair of an object of type
A and another FList<A> .
Now, repeat what you did in the case of the NaturalNumber and get:
FList<A> = 1 + A * FList<A>
= 1 + A * (1 + A * FList<A>) = 1 + A + A ^ 2 + A *
FList<A>
= 1 + A + A ^ 2 + A ^ 3 + A ^ 4 * FList<A>
...
This allows you to see FList<A> as a possible combination of all the possible
FList<A> s of length 0, 1, 2, 3 and so on.
The + here has the meaning of a logical OR, so an FList<A> is an empty FList
267
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
OR a single element of type A OR a pair of elements of type A OR a triplet of
elements of type A and so on.
FList<A> * (1 - A) = 1 =>
1
FList<A> = -------
(1 - A)
1
FList<A> = ------- = 1 + A + A^2 + A^3 + A^4 + .... + A^N + ...
(1 - A)
It’s curious how a complex data type like List<A> has an algebraic
relationship.
Key points
Algebra is a category of arithmetic that lets you combine numbers with
letters representing numbers by using specific rules.
You can think of a type as the Cartesian product of the types of its
properties. For instance, Pair<A, B> is the Cartesian product of A and B .
A Cartesian product of a type A and a type B is a new type, represented as
A × B . Its values are all the pairs (a, b) that you can create using a value
a from A and a value b from B .
The term isomorphic means “having the same form or structure”. Two
types, A and B , are isomorphic if a function of type Fun<A, B> maps each
value of A to one and only one value in B and vice versa.
Two isomorphic types are equivalent in the sense that you can use one or the
other without adding or removing any type of information.
268
Functional Programming in Kotlin by Tutorials Chapter 10: Algebraic Data Types
269
Functional Programming in Kotlin by Tutorials
11 Functors
Written by Massimo Carli
In Chapter 9, “Data Types”, you met the map function for the first time. You
learned that it’s one of the most important functions many data types provide
and that it’s related to the concept of a functor. But what is a functor, and why is
map so important? In this chapter, you’ll learn:
How to apply the concept of a functor to the category of types and functions.
What functor laws are and how to use equational reasoning to verify them.
What is a functor?
The definition of functor is quite simple. A functor is basically a way to map one
category in another while preserving the structure. But what does structure of a
category mean? The first step to understanding is a quick revision of the concept
of category.
Composition
Associativity
Identity
In Figure 11.1, you see an example of a category with two objects and one
morphism between them in addition to the identity morphisms that, by
definition, must be present for all objects.
270
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
ia
ib
Categories tend to have structures because objects and morphisms give them a
sort of pattern. For instance, you might have another category, like in Figure
11.2, that also has two objects and a morphism between them. Although they’re
two distinct categories, you can recognize that they have the same structure or
recall the same pattern.
ia
a c
d
ic
b
id
ib
This offers the chance to map a category into another one, keeping the same
structure, which recalls the same pattern. In the previous example, the pattern
has two objects and one morphism between them.
271
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
A functor, then, is something more than just mapping between two categories. It
must also preserve the structure. This means a functor has to map objects into
objects and morphisms into morphisms, following some rules.
a a' = Fa
b b' = Fb
C D
Figure 11.3: Mapping objects
Now, category C might have some structure you can represent with a morphism
f from a to b. The structure depends on the category, but in this example, you
can think of f as a single morphism from a to b. If this is the case, a functor also
has to map f into another morphism in the category D. This is where the
concept of preserving the structure becomes fundamental. If category C has a
morphism f from a to b, a functor must map it to another morphism, which you
represent as Ff, between a’ and b’.
You can also say that the functor must map the morphism f to a morphism Ff
from Fa to Fb.
272
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
a a' = Fa
f Ff
b b' = Fb
C D
Figure 11.4: Mapping morphisms
Note: It’s important to say that you might map f into another morphism
between two objects different from a’ and b’. That’s perfectly legal, but you
wouldn’t define a functor in that case.
F
Fa
a
g°f
Ff F( g ° f )
f
Fb
b
g Fg
c Fc
C D
Figure 11.5: Functor composition
273
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
The functor also maps the morphism f to Ff and the morphism g to Fg.
What makes F a functor is that F (g ◦ f) = Fg ◦ Ff. In other words, the functor of the
composition is the composition of a functor. This is the formal definition of the
preservation of structure concept.
Not all mappings between objects and morphisms work like this. On the
contrary, most don’t.
What’s true for a morphism f from a to b must be true for the entire hom-set
of all the morphisms from a to b.
In the case of identity, it must be true that F ia = i Fa. This means that a
functor must map identity morphisms ia for every object a to the identity for
the object Fa, which you can represent as i Fa.
The points above are the rules a functor must follow, usually referred to as the
functor laws. In short, a functor must preserve:
This is true for all the categories. But you, as an engineer, are interested in one
particular category: the category of types and functions.
274
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
Note: To better understand these concepts, take your time reviewing this
section as many times as you need. Try to imagine categories with different
structures and how the functor would change in each case.
Functors in programming
In the previous section, you learned what a functor is in terms of category
theory. But, as a programmer, you’re particularly interested in one category: the
one where objects are types and morphisms are functions. In this case, you’re
working with endo-functors. The “endo” prefix means “internal” and
emphasizes that the functor maps types in types and functions in functions.
In your context, then, a functor maps types to types. It’s crucial to note that this
must work for any type in the category. To represent a functor, you need a way
to declare the type as a parameter and replace that parameter with the specific
type you need. The good news is that you already know how to do this using
generic types and type parameters. If F is your functor, you’ll represent it as
F<A> where A is the type parameter. You often call F<A> a type constructor
because you create a type, F<A>, starting from the type A.
But a functor doesn’t map just objects — it also must map functions. How can it
do that? The best way to understand this is with an example. Open Optional.kt
in this chapter’s material, and look at the code you implemented in Chapter 9,
“Data Types”:
companion object {
@JvmStatic
fun <T> lift(value: T): Optional<T> = Some(value)
@JvmStatic
fun <T> empty(): Optional<T> = None
}
}
object None : Optional<Nothing>()
data class Some<T>(val value: T) : Optional<T>()
275
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
The Optional<T> data type provides the context of some object that can be
present or not. To make things clearer, the Optional here is the F you’ve used
so far to represent a functor, and Optional<T> is its type constructor, F<T>.
When you replace the type parameter, you can have Optional<Int> ,
Optional<Boolean> , Optional<User> , etc. What you’ll define for
Optional<T> will be valid whatever T ’s specific type is.
Note: As you learned in Chapter 9, “Data Types”, you can use functions like
lift to create an Optional<T> from an object of type T or, in general, an
F<T> from a T .
Now, look at Figure 11.4 and assign a specific meaning to every object and
morphism. In the source category, you have two types, A and B , and a
function f from A to B of type Fun<A, B> . You know how to map the type
A to Optional<A> and the type B to Optional<B> . To make Optional<T> a
function, you need a way to map the function f of type Fun<A, B> to a
function from Optional<A> to Optional<B> of type Fun<Optional<A>,
Optional<B>> . This is precisely the map function you implemented in Chapter
9, “Data Types”, like this:
276
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
fun <A, B> Optional<A>.map(fn: Fun<A, B>): Optional<B> =
when (this) {
is None -> Optional.empty()
is Some<A> -> Optional.lift(fn(value))
}
The function map above has the right type, but how can you be sure the
Optional<T> with the previous map is actually a functor? You need to prove
the functor laws.
Functor laws
In the previous section, you proved that the map function you implemented in
Chapter 9, “Data Types”, maps functions of type Fun<A, B> into functions of
type Fun<Optional<A>, Optional<B>> . Previously, you learned how to create
an Optional<A> from A using a type constructor or a function like lift .
Unfortunately, this isn’t enough to prove that, with the map function,
Optional<T> is now a functor.
To prove that Optional<T> is a functor, you need to prove the functor laws,
and specifically, that it preserves composition and identity.
To prove that your Optional<T> with the map you implemented is a functor,
you need to prove that:
F ia = i Fa
F (g ◦ f) = Fg ◦ Ff
Proving these for the Optional<T> data type is a useful exercise. To do so, you
use a technique called equational reasoning. Equational reasoning works here
because Kotlin allows you to define pure functions as equalities. The left side is
equal to the right side, and you can use the substitution model — as you learned
in Chapter 3, “Functional Programming Concepts” — to replace the invocation
of the function with the expression itself. Replacing the function invocation with
the expression it represents is a technique called inlining. Of course, equality is
277
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
symmetric, so you can also replace an expression with the invocation to the
related function. In this case, you use a technique called refactoring.
Note: Not all programming languages provide tools to easily prove the
functor laws. Sometimes, other programming languages provide unit tests to
prove the same properties more empirically.
F ia = i Fa
companion object {
@JvmStatic
fun <T> lift(value: T): Optional<T> = Some(value)
@JvmStatic
fun <T> empty(): Optional<T> = None
}
}
This definition says that an Optional<A> can be None or a Some<T> . You also
defined map like this:
278
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
What you need to do is replace the function fn with an identity and see if you
get the identity for Optional<T> . That would then be a function that returns
exactly the same Optional<T> you pass as an input parameter.
Note that because id is the identity function, you can replace the type B with
A . Given that id(value) = value , you get:
Or:
F (g ◦ f) = Fg ◦ Ff
In this case, you also have two possible cases. If your Optional<T> is None ,
the map function becomes:
279
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
This means that if you have None , whatever function you apply, you always get
None . This is true if you apply just f , g or the composition of the two. In the
case of None , the left and right members are then the same.
This means that you basically lose the option for the case when the value isn’t
present. You remove the effect of the context, which consists in removing the
F from the equation, getting:
g ◦ f = g ◦ f
This is true by definition, so you can conclude that the Optional<T> and map
function you created defines a functor.
As you know, if you have two functions, f and g , and any functor F , the
following equivalence is true:
F ( g ◦ f ) = Fg ◦ Ff
In the case of FList<T> and List<T> , you can write this as:
280
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
side of the previous equation.
3. Invoke map , passing the composition of f and g as a parameter.
The functor laws say that the two sides of the equation are the same. The good
news is in terms of performance. If N is the length of the list, the left side has
complexity O(2N) while the right side has complexity O(N) . In terms of
complexity, a constant doesn’t matter: The composition g ◦ f takes
approximately the same time as invoking f first and then g . In this case, you
still have a single iteration, and the equivalent provides you the opportunity to
write more readable code.
Bifunctors
In the previous sections, you learned what a functor is in terms of category
theory, and you proved the functor laws for the Optional<T> data type. You
also saw that knowing FList<T> and List<T> are functors helps you
structure your code in an easier — and, probably more performant — way.
Optional<T> , FList<T> and List<T> all have a single type parameter. In
Chapter 9, “Data Types”, you also implemented Either<A, B> as an example of
a data type with multiple type parameters. So, what’s the relationship between
the concept of a functor and Either<A, B> ?
C D
c
a
f g
d
b
281
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
CxD F E
( a, c ) m
( f, g ) F( f, g )
( b,d ) n
The category C×D, which is the Cartesian product of the category C and D.
This means each object in the category C×D is a couple of objects, (c, d),
where c is an object of C, and d is an object of D.
The main difference is that now the type constructor contains two different type
parameters, not just one, which is why it’s called a bifunctor.
The relationship between bifunctors and algebraic data types you learned in
Chapter 10, “Algebraic Data Types”, is intriguing. As you know, the type
Pair<A, B> is an example of the product of types, while Either<A, B> is an
example of the sum. In both cases, you can define a function, bimap , like the
following one you already implemented in Either.kt in this chapter’s material:
282
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
the following code:
It’s essential that you always invoke both fl and fr on the first and
second properties, respectively, getting the object of type Pair<C, D> in
return.
You won’t prove it in this book, but the good news is that you can define a
functor or bifunctor for any algebraic data type.
Typeclasses
In the last three chapters, you learned how to implement some of the most
important data types and saw how to make them work as functors. When you
say that List<T> is a functor, you know that you can use a map function
accepting another function as a parameter, and the functor laws will be valid. If
you have multiple type parameters, you can say the same things about
bifunctors and the bimap function.
It also means that many data types have some common behavior. You usually
refer to this common behavior with the concept of a typeclass. A functor is then
a typeclass. In the following chapters, you’ll learn about other typeclasses like
monoids, semigroups, monads and so on.
Key points
A functor is a way to map one category to another while preserving their
structures.
You define the structure of a category using objects and morphisms.
283
Functional Programming in Kotlin by Tutorials Chapter 11: Functors
A type constructor is the mechanism a language provides to define a new
data type using generics.
A functor mapping objects and morphisms from the category C×D is called a
bifunctor.
284
Functional Programming in Kotlin by Tutorials
In this chapter, you’ll learn everything you need to know about a couple of very
important typeclasses. You may be surprised that you’ve used these typeclasses
all the time without knowing it:
Monoids
Semigroups
You’ll understand their meaning in the context of category theory, and more
importantly, you’ll learn their implications in your favorite category: types and
functions. In particular, you’ll see:
What is a monoid?
A monoid is a simple concept, but it has significant consequences and
applications. To define a monoid, you need:
A set of objects.
A binary operation.
Be associative.
Have a unit value.
Being associative means that, given the elements a, b and c and the operation op,
285
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
the following property must always be true:
a op (b op c) = (a op b) op c
The unit for the operation op is a particular element of the set that, whatever the
element a is, makes the following equivalences always true:
a op unit = a
unit op a = a
Addition.
a + (b + c) = (a + b) + c
The particular integer value that’s the unit for the addition is, of course, 0. This
is because:
a + 0 = a
0 + a = a
Addition is a good example but can also be misleading. For instance, addition is
commutative, which means that:
a + b = b + a
Exercise 12.1: Can you find an example of a monoid whose operation isn’t
commutative? Remember, you can find the solutions for all exercises and
challenges in Appendix K.
Exercise 12.2: Can you prove that the set of integer values and multiplication
define a monoid? In this case, what would the unit element be?
286
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
From the previous definition, you understand that you can have many different
types of monoids using the same set but a different operation or vice versa. But
then, how would you define the typeclass Monoid in code?
Note: It’s important to understand that you could do this in multiple ways.
The method you’ll implement here is just one possibility.
Open Monoid.kt in this chapter’s material, and add the following code:
interface Monoid<T> { // 1
val unit: T // 2
val combine: (T, T) -> T // 3
}
The previous definition has a few significant things to note that have
consequences on some implementation details. You might have the following
questions in particular:
combine has two input parameters of type T and returns another value of
the same type T . You learned that composition is easier with functions with
a single parameter. How, then, can you improve the Monoid<T> definition?
Monoid<T> is an interface that a type might implement to provide unit and
combine implementations. But you also know that you might have two
monoids for the same set of values. For instance, multiplication and addition
are both monoids on Int . How could you then provide a different monoid
implementation for the same type T ?
At compile time, there’s no way to force the validity of the property about
associativity and unit. This depends on the combine implementation. What
can you do, then, to have confidence your implementation will work
properly?
287
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
fun <A, B, C> Fun2<A, B, C>.curry(): (A) -> (B) -> C = { a: A ->
{ b: B ->
this(a, b)
}
}
curry allows you to represent a function of two input parameters of type (A,
B) -> C as a higher-order function of a single parameter that returns another
function of type (A) -> (B) -> C . This suggests that you can replace the
previous Monoid<T> definition with the following in the same Monoid.kt:
interface Monoid<T> {
val unit: T
val combine: (T) -> (T) -> T // HERE
}
As you can see, now combine has a single input parameter of type T and
returns a function of type (T) -> T . But you can do even better by replacing
the previous definition with the following:
288
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
3. combine as a function of type Int.(Int) -> Int which, as you’ll see very
soon, might have some advantages in Kotlin. If it’s more familiar, you might
also use the previous definition of Monoid<T> with combine of type (T) ->
(T) -> T .
Exercise 12.3: How would you implement the monoid MonoidIntMult for
Int and multiplication? Then, check out the solution in Appendix K.
Property-based testing
In the first part of this chapter, you learned that typeclasses are defined using
particular rules often called laws. You already met them in Chapter 11,
“Functors”. You also created some very simple Monoid<T> implementations
that you’re confident follow the monoid rules, but how can you be sure of that?
Answering this question is an ideal opportunity to introduce a technique called
property-based testing.
As you know, testing is one of the most challenging parts of the development
process of any piece of software. To understand why, just look at the code in
PropertyTest.kt:
289
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
The question now is: How can you test this code? sum is a basic function that
adds two values you pass as input. The first approach is to implement some unit
tests. You can implement some tests like the following. Add this code to
PropertyTestTest.kt:
class PropertyTestTest {
@Test
fun `sum test using predefined values`() {
Truth.assertThat(sum(2, 3)).isEqualTo(5)
Truth.assertThat(sum(2, 5)).isEqualTo(7)
Truth.assertThat(sum(-2, 5)).isEqualTo(3)
}
}
Run the tests by clicking the icon in Figure 12.1, and you’ll get what’s in Figure
12.2, proving that all the tests pass.
But you’re only testing some of the possible values! What about all the other
possible inputs? You could add more cases, but how many tests do you actually
need to implement to be sure your function is correct?
In this specific case, sum accepts two parameters of type Int . This means the
possible combinations in input are the number of elements in the Cartesian
product Int × Int , which has 4,294,967,295 × 4,294,967,295 elements! Of
course, you can’t implement all those tests, so you need a smarter solution.
What about generating some random values and checking whether sum works
correctly?
290
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
@Test
fun `sum test using random values`() {
val firstValue = Random.nextInt() // 1
val secondValue = Random.nextInt() // 2
val expectedValue = firstValue + secondValue // 3
Truth.assertThat(sum(firstValue, secondValue)) // 4
.isEqualTo(expectedValue)
}
1. Use Random.nextInt to get a random Int for the first parameter you store
in firstValue .
2. Do the same for the second parameter, which you put in secondValue .
4. Check that the value you get from sum is what you expected.
If you run it once, you might’ve just been lucky. A possible option is to run the
test more times. Add the following code in PropertyTestTest.kt, and run it
again:
@Test
fun `sum test using random values 100 times`() {
100.times {
val firstValue = Random.nextInt()
val secondValue = Random.nextInt()
val expectedValue = firstValue + secondValue
Truth.assertThat(sum(firstValue, secondValue))
.isEqualTo(expectedValue) o
}
}
291
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
This seems fine, but it’s not quite so simple. Can you see why?
@Test
fun `sum test using random values`() {
val firstValue = Random.nextInt()
val secondValue = Random.nextInt()
val expectedValue = firstValue + secondValue // HERE
Truth.assertThat(sum(firstValue, secondValue))
.isEqualTo(expectedValue)
}
To test sum , you’re re-implementing the same feature! How can you test that
sum is correct if you compare its result with a value you get by doing exactly
the same thing? Of course, it passes — but they might both be wrong.
So the big problem now is: How would you test sum without:
The first step is to think about how sum is different from other operations. For
instance, you previously learned that addition is commutative, meaning that for
any a and b:
a + b = b + a
292
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
You know that this isn’t true for all operations. Subtraction, for example, is not
commutative. You can then add the following test to PropertyTestTest.kt:
@Test
fun `test sum is commutative`() {
100.times {
val firstValue = Random.nextInt() // 1
val secondValue = Random.nextInt() // 1
val result1 = sum(firstValue, secondValue) // 2
val result2 = sum(secondValue, firstValue) // 3
Truth.assertThat(result1).isEqualTo(result2) // 4
}
}
3. Invoke sum with the same parameter values but in a different order.
4. Check that the results you got in the two cases are the same.
If you run this test, you’ll see it passes. This is an improvement over the
previous solutions, but unfortunately, it’s not enough. You can easily test this by
replacing the + with * in PropertyTest.kt:
The previous test still passes because, like addition, multiplication is also
commutative. You don’t just give up, then, wondering what the difference is
between addition and multiplication.
There are many, but a possible solution is that adding 1 twice to any value a is
the equivalent of adding 2 to the same a. This isn’t true with multiplication.
Multiplying by 1 twice isn’t equivalent to multiplying by 2 once. To spot the
multiplication you introduced earlier, add the following test to
PropertyTestTest.kt:
@Test
fun `test addition is not multiplication`() {
100.times {
val randomValue = Random.nextInt() // 1
val result1 = sum(sum(randomValue, 1), 1) // 2
val result2 = sum(randomValue, 2) // 3
Truth.assertThat(result1).isEqualTo(result2) // 4
}
}
293
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
Run this test using the bugged sum implementation, and you’ll get what’s in
Figure 12.5:
You can fix the problem in the sum implementation and restore this in
PropertyTest.kt:
Now, comment out all the tests, keeping just the following, which you
implemented as property-based tests:
class PropertyTestTest {
// ...
@Test
fun `test sum is symmetric`() {
100.times {
val firstValue = Random.nextInt()
val secondValue = Random.nextInt()
val result1 = sum(firstValue, secondValue)
val result2 = sum(secondValue, firstValue)
Truth.assertThat(result1).isEqualTo(result2)
}
}
@Test
fun `test addition is not multiplication`() {
100.times {
val randomValue = Random.nextInt()
val result1 = sum(sum(randomValue, 1), 1)
val result2 = sum(randomValue, 2)
Truth.assertThat(result1).isEqualTo(result2)
}
}
}
294
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
Run them, and you’ll see them all passing, like in Figure 12.6:
Everything looks fine, but you still need to fix something. Just replace the sum
implementation with the following in PropertyTest.kt:
Because sum always returns the same value, all the previous tests pass! Of
course, this isn’t good. The output must be a function of the input. You don’t
want to fail in the very first tests you wrote when you knew exactly what values
to pass as input, and you had to re-implement the same sum in the tests.
A possible solution to this is the use of a special value that allows you to
somehow predict the result without knowing all the input values. To understand
what this value is, add the following test to PropertyTestTest.kt:
@Test
fun `test using unit value for addition`() {
100.times {
val randomValue = Random.nextInt() // 1
val result1 = sum(randomValue, 0) // 2
val expected = randomValue // 3
Truth.assertThat(result1).isEqualTo(expected) // 4
}
}
When you run the previous test with the last bugged sum implementation,
you’ll see the test fails, like in Figure 12.7:
295
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
It’s crucial now to understand what you actually did. Instead of implementing
unit tests using specific input values, you focused on the main properties of
addition and proved that they’re valid regardless of the input values. This is the
idea behind the concept of property-based testing.
You might wonder if this idea can somehow be abstracted and generalized. The
answer is yes!
Note: Some frameworks, like Kotest, allow you to use property-based testing
in your code in a more robust and complete way. In this case, you’ll just
implement some of the main abstractions as an example of the technique. It’s
up to you to decide if you want to use Kotest, another framework or
implement your own.
296
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
In the same file, add the following implementation for Int values:
Now, you need a way to represent properties like commutativity, associativity and
so on. A simple way to do this is adding the following definition in
PropertyTest.kt:
interface Property<T> { // 1
operator fun invoke( // 2
gen: Generator<T>, // 3
fn: (List<T>) -> T // 4
): Boolean // 5
}
4. Need a function of the generated input values List<T> that returns another
value of type T . The return type could be different in case you’d need to test
more complex properties.
5. Require Boolean as the return type for invoke . This tells you whether the
property is verified. Again, you’re keeping things simple, but in Chapter 14,
297
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
“Error Handling With Functional Programming”, you’ll see how to get more
information from a failing property verification using Either<A, B> or
Kotlin’s built-in Result<T> .
3. Invoke the function fn you get as invoke ’s input parameter, passing the
random values in the same order you got from Generator<T> .
4. Do the same, but using the random values you got from Generator<T> in a
different order.
5. Check if the two results are the same. This is required to prove the
commutative property.
Now, in a very similar way, you can implement Property<T> for associativity.
In PropertyTest.kt, add the following code:
298
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
2. Invoke the function fn you get as invoke ‘s input parameter like op(op(a,
b), c) , assuming a , b and c are the 3 random values, and op is the
operation you’re testing.
4. Verify that the results in the two cases are the same.
Finally, you can implement Property<T> for the identity property by adding
the following code in PropertyTest.kt:
class IdentityProperty<T>(
private val unit: T // 1
) : Property<T> {
override fun invoke(
gen: Generator<T>,
fn: (List<T>) -> T
): Boolean {
val randomValue = gen.generate(1)[0] // 2
val res1 = fn(listOf(randomValue unit)) // 3
val res2 = fn(listOf(unit, randomValue)) // 4
return res1 == randomValue && res2 == randomValue // 5
}
}
1. Need the unit element, which depends on the specific type T and
operation.
299
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
3. Simply invoke the receiver Property<T> and the rightProp you get as an
input parameter. This means that the returning Property<T> will evaluate
to true if and only if both the receiver property and the one you pass as
rightProp evaluate to true .
@Test
fun `Property-based test for sum`() {
100.times {
val additionProp =
CommutativeProperty<Int>() and // 1
AssociativeProperty() and
IdentityProperty(0)
val evaluation = additionProp(IntGenerator) { // 2
sum(it[0], it[1])
}
Truth.assertThat(evaluation).isTrue() // 3
}
}
3. Confirm that all the properties are verified by testing the value of
evaluation , which must be true.
Run the previous test, and you’ll see that all the tests pass! You can also verify
300
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
that the test fails if you change the sum implementation like you did above.
Exercise 12.5: In the previous section, you proved that addition is different
from multiplication using op(op(a), 1) and op(a, 2) . The two expressions
are equal for any Int a if op is addition, but the same isn’t true if op is
multiplication. Can you implement a Property<Int> implementation for this
rule and use it to create a new test?
You can find the solution in Appendix K and the challenge project for this
chapter.
A crucial aspect of what you just did is that the properties you’ve defined for
sum aren’t just properties you use for testing. They’re actually the specification
for sum or addition in general. Every operation that satisfies
CommutativeProperty , AssociativeProperty and IdentityProperty is
addition.
In this chapter, you’ve learned what property-based testing is. You also
implemented a very small framework with the goal of having a way to verify if
your monoid implementations work. Property-based testing is useful with
monoid laws but is also one of the main techniques for verifying any typeclass
law.
301
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
Add this to Monoid.kt if not already present in your exercise solution:
class StringGenerator(
private val minLength: Int = 0,
private val maxLength: Int = 10
) : Generator<String> {
val chars = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"1234567890!±§!@£$%^&*()_+-="
override fun generate(n: Int): List<String> = List(n) {
val length = Random.nextInt(minLength, maxLength)
val currentString = StringBuilder()
(1..length).forEach {
currentString.append(
chars[Random.nextInt(0, chars.length)])
}
currentString.toString()
}
}
Identity
Hey, you already have them! In the test build type, create a new file named
StringMonoidTest.kt, like in Figure 12.8:
302
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
class StringMonoidTest {
@Test
fun `test string concat using generators`() {
100.times {
val stringConcatProp =
AssociativeProperty<String>() and // 1
IdentityProperty("") // 2
val evaluation = stringConcatProp(StringGenerator()) { // 3
MonoidStringConcat.combine(it[0], it[1]) // 4
}
Truth.assertThat(evaluation).isTrue() // 5
}
}
}
Great! You managed to implement a monoid and test its properties using
property-based testing.
But why are monoids so important? Where do you actually use them?
303
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
In Chapter 9, “Data Types”, you implemented two of the most important
functions a data type usually provides: fold and foldRight . These are very
helpful functions you can use, for instance, to calculate the sum of the values in
a List<Int> , like the following in Foldable.kt:
fun main() {
listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).sumList() pipe ::println
"supercalifragilisticexpialidocious".reverseString() pipe
::println
}
55
suoicodilaipxecitsiligarfilacrepus
As you see, fold and foldRight both need an initial value and a combine
function that’s reminiscent of the concept of Monoid<T> . It’s crucial to note
how a Monoid<T> has a single type parameter T , so the combine has type
(T, T) -> T . This makes fold and foldRight basically the same.
For this reason, it’s often useful to create an abstraction for any object with a
fold function like this, which you should add in the same Foldable.kt file:
304
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
fun <T> Foldable<T>.fold(monoid: Monoid<T>): T =
fold(monoid.unit, monoid.combine)
Once your data type implements Iterable<T> , it also has the fold function
accepting a Monoid<T> as an input parameter.
For example, you can implement the previous sumList function like this:
1. Use a Monoid<String> .
305
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
2. Convert the Char you get as an input parameter for the fold lambda in a
String .
fun main() {
listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).sumList() pipe ::println
"supercalifragilisticexpialidocious".reverseString() pipe
::println
}
You get:
55
supercalifragilisticexpialidocious // WRONG
Here, you see that the reverseString doesn’t do its job. But you can fix this
easily with the following implementation that uses commutate to invert the
combine function of the MonoidStringConcat you pass as an input parameter:
fun String.reverseString() =
fold(MonoidStringConcat.commutate())
Run main again, and now you’ll get what you expect:
55
suoicodilaipxecitsiligarfilacrepus // OK!
306
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
i m
This is a category with a single object, m. Of course, because it’s a category, you
have an identity im, but you also have many other morphisms from m to itself.
You also have composition, so you can compose all those morphisms, getting
other morphisms. Compared to other categories, in this case, all the morphisms
are composable because they all start and end at m.
The uniqueness of the object in this category is the characteristic that gives it the
name monoid.
You already learned that a monoid needs a set of values and a binary associative
operation along with a special value called a unit. How is this definition related
to the one you saw in category theory?
Look at Figure 12.10, and you see that the category with one object, m, has only
one hom-set, M(m, m). Remember, the hom-set is the set of all morphisms
between objects that, in this case, are equal to m, which is the only object of the
category. Because that’s a category, you say that for every couple of morphisms
in M(m, m), another morphism exists: the composition of the two that you can
call, for instance, multiplication. The composition is also part of M(m, m). This
is equivalent to the combine you met in the first — and more familiar —
definition of monoid.
In M(m, m), you also have a special morphism, called unit, that you compose
with any other morphisms m, getting m itself. This is true because every
category must have the identity morphism.
It’s surprising how all the concepts you’ve seen in the previous examples can be
explained using objects and morphisms in category theory.
Looking at Monoid<T> , you might ask if the unit is always necessary, and the
answer is no. In the case of fold , you used the unit as a possible initial value.
What if you don’t need it?
In this case, the typeclass that defines a set of values and an associative binary
operation is a semigroup you can represent like this in Semigroup.kt:
308
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
This allows you to update the definition of Monoid<T> in Monoid.kt like this:
fun main() {
val listA = listOf(1, 2, 3, 4, 5, 6)
309
Functional Programming in Kotlin by Tutorials Chapter 12: Monoids & Semigroups
val listB = listOf(3, 5, 6)
mergeAndCombine(listA, listB, SemigroupIntMult) pipe ::println
}
Getting in output:
Key points
A monoid is a set of values with an associative binary operation and a unit
element.
The existence of the associative binary operation and the unit element are the
monoid laws.
Monoids work very well with Foldable data types, which provide
implementations for fold and foldRight .
310
Functional Programming in Kotlin by Tutorials
13 Understanding Monads
Written by Massimo Carli
For this reason, in this book, you’ll look at monads in a pragmatic way. Here,
you’ll start from the problem of the composition of functions in the Kleisli
category and prove that monads are a beautiful way to do it.
Meet the fish operator, >=> , and the bind operator, >>= .
Revise the concept of functor using the functor laws in the implementation
of fish and bind .
311
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
pragmatic approach to solving a simple problem. Given two functions:
You want to define a new function of type (A) -> M<C> that’s the composition
of the two. What you represent with M<A> is a generic typeclass you usually get
from A using a type constructor. Examples of M<A> are List<A> or
Optional<A> .
Note: One way to abstract any possible typeclass M<A> is called higher kind.
At the moment, Kotlin doesn’t provide this feature. But some frameworks —
like Arrow, which you’ll learn in Chapter 19 — provide tools to generate
something similar. Arrow, for instance, uses the @higherkind annotation to
generate a class Kind<ForM, T> as an abstraction of M<T> . With this,
Optional<A> would become Kind<ForOptional, A> where ForOptional is a
placeholder representing the Optional<A> type. The Optional type also
generated from this Kind . For right now, don’t worry about it. You’ll learn
everything about this in Chapter 19, “Arrow”. The only thing you need to
understand is that what you’ll create using M<A> or Kind<ForM, A> will be
valid for any specific typeclass.
What you want to implement isn’t a classic composition between two functions
because the output type for f is M<B> , but the input type for g is B . Solving
this problem is precisely what will allow you to understand what a monad is.
Note: In this chapter, you’ll represent generic types like M<T> . Sometimes,
you’ll represent the same type with M<A> , M<B> or using any other letter for
the type parameter like M<X> . When defining a function, which letter you use
doesn’t matter. As an example, this means you can define a lift like:
Or like:
312
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
... }
What is a category?
As you’ve learned so far, a category is the model you use to study the theory of
composition. A category is a bunch of objects and some arrows between them
called morphisms that follow some fundamental properties:
1. Composition
2. Associativity
3. Identity
There are different types of categories, but the one that’s most important for
you is the category of types and functions. For this category, the previous
properties become:
1. For every pair of functions f of type (A) -> B , and g of type (B) -> C ,
there must be a function of type (A) -> C that’s the composition of f and
g . You usually represent this function as g ◦ f , which you read as “g after
f”, or as f compose g , where compose is a function you defined as:
2. For all functions f of type (A) -> B , g of type (B) -> C , and h of type
(C) -> D , you can say that:
h ◦ (g ◦ f) = (h ◦ g) ◦ f
3. For every type A , there’s always a function of type (A) -> A . You call this
identity and represent it as ia . It has the following property for every
function f :
313
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
ia ◦ f = f ◦ ia = f
But why are functions and compositions so important? You already learned that
it’s easier to understand programs if you decompose them into many small
pieces, as you can implement and test them more easily. All these pieces are
functions. To build your program, you need to put all the functions together
using composition.
The properties of a category are definitions you need to remember and digest to
really understand how particular functions compose themselves. Some
functions are special and allow you to define fundamental categories. One of
these is the Kleisli category.
In the previous chapters, you learned that you can start from a type A and
create a new type you represent as M<A> using a type constructor. For
instance, given a type A , you can define the type Optional<A> or the type
List<A> . In the first example, you replaced M with Optional , and in the
second, M with List . To get an M<A> from A , you defined the lift
function. M is also the way you define a typeclass.
Note: From this point, you’ll use M<A> to define any new type you create
from A using a type constructor. As mentioned earlier, to do so, you’d need a
generic way to represent any M<A> . To ensure the code you’ll write compiles,
use a simple workaround: You’ll represent M<A> as a typealias of some
existing typeclass.
314
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
An example of these functions have type (A) -> Optional<B> , (A) ->
List<B> or the (A) -> Pair<B, String> you defined as Writer<A, B> in
Chapter 3, “Functional Programming Concepts”.
The Writer<A, B> type is a perfect example because you implemented the
composition using the following code:
Because Writer<A, B> is just a typealias of (A) -> Pair<B, String> , you
might think of it as a special case of (A) -> M<B> where M<B> is Pair<B,
String> .
Note: Second spoiler alert: Writer<A, B> with the after function is a
monad!
Note: Yes, you really call that operator “fish”! This is the name Bartosz
Milewski gave it in his Category Theory course on YouTube. The reason is that
it looks like a shark. You’ll give it a more formal name later.
((A) -> M<B>) >=> ((B) -> M<C>) -> ((A) -> M<C>)
315
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
Or
f >=> g
The result of f >=> g is a function of type (A) -> M<C> . To better understand
how to implement this operator, it’s crucial to note that the output type for f is
M<B> , while the type for the input of g is B . There’s some type impedance
because B isn’t M<B> . The goal is to define what you really need to create an
implementation of the fish operator for all the typeclasses M .
What you need to get is a function of type (A) -> M<C> , which is a function
from A to M<C> . Open Monad.kt in the material for this project and add the
following code:
At the moment, you just know that you need to return a function of A , so you
define it as a lambda with a single parameter a of type A . Because you have
f , the first — and probably the only — thing you can do now is apply it to the
input a of type A like this:
Here, mb has type M<B> , which you defined explicitly to make the code
316
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
clearer.
Now, you have g , which has type (B) -> M<C> . The question is: How can you
use mb of type M<B> and g of type (B) -> M<C> to get a value of type M<C> ?
At this point, you don’t know it yet, but you can delegate this operation to
another operator called bind , which you represent with >>= .
Note: Kotlin doesn’t allow you to define operators with the names >=> or
>>= . This is why you’ll use the names fish and bind .
Represent the type of >>= with the following code you add in Monad.kt:
Note: As you can easily verify, the previous code compiles as soon as you use
M<A> as a typealias of List<A> . This is also true for any other type that — as
you’ll see later — provides a map function or, in other words, is a functor.
These two sentences are very powerful because they allow you to have a first
pragmatic definition of monad.
317
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
Given bind and lift , you can implement the fish operator and compose
two functions in the related Kleisli category. This means that a possible way to
define a monad is through a Monad<T> interface, like the following you add to
Monad.kt:
interface Monad<T> {
Implementing bind isn’t so obvious, but you can make the implementation
easier as soon as you realize that M<T> is a functor!
What is a functor?
In Chapter 11, “Functors”, you learned that a functor is essentially a way to map
objects and morphisms of a category C in objects and morphisms in a category
D following some rules that you call functor laws.
You probably remember that a functor preserves structure, and you represent this
with the following properties:
The first law says that a functor maps the morphism g ◦ f composition of f and g
in the composition of the morphisms Fg and Ff.
The second law says that a functor maps the identity of the source category in
the identity of the category you use as the destination.
The good news now is that the types you’re representing with M<A> are exactly
the functor you called Fa before. If you think of the objects for the source
category types A and the objects of the destination category as M<B> , you
318
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
understand that a function of type (A) -> M<B> is exactly the functor that
maps an object of the source category to the object of the destination category.
F
Fa
a
g°f
Ff F( g ° f )
f
Fb
b
g Fg
c Fc
C D
Figure 13.1: Mapping objects
As you see in Figure 13.1, the composition of Ff and Fg is equal to F (g ◦ f). You
can write this like:
Fg ◦ Ff = F (g ◦ f)
To make the concept more familiar, replace F with M and see that:
3. Composing a function f of type (A) -> B with g of type (B) -> C in the
category C is equivalent to composing the two functions of type (M<A>) ->
M<B> and (M<B>) -> M<C> in D.
4. Finally, the functor laws say that composing f and g and then lifting to M is
equivalent to lifting f and g using M and then composing.
319
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
3. Invoke map on the value of type M<B> , passing g as a parameter and getting
a value of type M<C> .
The functor laws say that the values of type M<C> you get in the two distinct
ways are exactly the same.
Now, you’ll use these properties to simplify the implementation of the bind
operator, >>= .
Monads as functors
You want to find an easier way to implement bind for all the M<B> . What the
bind operator does is apply g to a value of type M<B> . Because M<B> is a
functor and g is of type (B) -> M<C> , you can apply g to M<B> , invoking
the map function. This is because a functor rule says that lift and map is
equivalent to map and lift .
In other words, given a type A , you can first lift it to M<A> and then apply a
function f of type (A) -> B using M<A>.map(f) . Or you can first apply the
function f to the value of type A and get a value of type B and then lift it,
getting an M<B> .
As defined explicitly, the type of the temporal variable tmp is M<M<C>> . What
you get from map is a double embellishment. As you did earlier, you can think of a
function, called flatten , that does exactly what its name states. It flattens the
type M<M<A>> to a single M<A> . Now, add the following function to Monad.kt:
320
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
And change bind like this:
Note: Here, you’re assuming that M<A> has the map function following the
functor laws. If you’ve used M<A> as a typealias of List<A> , this comes for
free.
With this, you can also simplify the implementation for fish like this:
You basically need to define the following function in the ListMonad.kt file you
find in the material for this chapter:
This function starts from a List<List<T>> and must return a List<T> . One of
the implementations to do that is the following, which you can add to the same
ListMonad.kt:
321
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
val intToChars =
{ n: Int -> List(n) { 'a' + n } } // 2
fun main() {
val fished = countList listFish intToChars // 3
fished(3) pipe ::println
}
322
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
[b, c, c, d, d, d]
Of course, this is just an example, and it doesn’t really matter what the two
functions you’re composing do. The important thing is that listFish works.
But, what’s the logic of listFish ?
The map and flatten names should remind you of a function called
flatMap , which you met in Chapter 7, “Functional Data Structure”. This is the
same function, and what you learned here is the reason for its name.
fun main() {
// ...
countList(3).flatMap(intToChars) pipe ::println
}
Run the code now, and you get exactly the same result:
[b, c, c, d, d, d]
Exercise 13.1: How would you make the Optional<T> data type you created
in Chapter 9, “Data Types”, a monad? If you need help, you can check out the
solution in Appendix L.
Exercise 13.2: What’s the relation between the fish operator, >=> , and
flatMap ? Can you express the latter in terms of the former for Optional<T> ?
A solution is in Appendix L if you need it.
Why monads?
This is all fascinating, but why do you really need monads? The answer is in the
problem monads solve and precisely in the composition of what you call Kleisli
arrows and represent as a function of type (A) -> M<B> .
323
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
In Chapter 3, “Functional Programming Concepts”, you learned the difference
between a pure and impure function. A pure function:
You also learned that you can transform a function with side effects into a pure
function by just making the effect part of the returning type. The Writer<A,
B> type is a great example of this. In Exercises 13.1 and 13.2, you implemented
flatMap for the Optional<T> type. It’s basically a method to compose
functions of type (A) -> Optional<B> .
A partial function is a function that isn’t valid for all the values in its domain. A
very simple example is the following, which you should write in
OptionalMonad.kt:
This is a very simple function that converts a String in an Int . Of course, not
all the String s contain a value that can be converted into Int s. Run:
fun main() {
strToInt("123") pipe ::println
}
123
fun main() {
strToInt("onetwothree") pipe ::println
}
324
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
2. Convert the String value to Int and lift the result in the case of
success.
Now, run the main again, and you get something like this:
com.raywenderlich.fp.exercise1.None@372f7a8d
Besides the fact that toString() isn’t implemented for None , you see that you
get None as the result. A test for this would now be as easy as writing in the
OptionalMonadKtTest.kt file in the material for this project:
class OptionalMonadKtTest {
@Test
fun `When input is not valid returns None`() {
assertThat(strToInt("onetwothree")).isEqualTo(None)
}
}
325
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
Once you have a pure strToInt , you might need to compose it with another
function that also returns an Optional<T> . A possible example of a function
you might need to compose with the previous is the following, which you can
add to OptionalMonad.kt:
How can you then compose strToInt with root ? The answer is simple now,
and it’s one you accomplish with the following code:
fun main() {
val strToRoot = ::strToInt optionalFish ::root // 1
strToRoot("onetwothree") pipe ::println // 2
strToRoot("123") pipe ::println // 3
}
Here, you:
com.raywenderlich.fp.exercise1.None@1f32e575
Some(value=11.090536506409418)
If you want to use the flatMap you implemented in Exercise 13.2, run the
following code:
fun main() {
// ...
strToInt("onetwothree").flatMap(::root) pipe ::println
strToInt("123").flatMap(::root) pipe ::println
}
326
Functional Programming in Kotlin by Tutorials Chapter 13: Understanding Monads
Getting the same output:
com.raywenderlich.fp.exercise1.None@1f32e575
Some(value=11.090536506409418)
Monads are important because they allow you to compose functions that handle
side effects as part of the return type.
Key points
A monad is a monoid in the category of endofunctors.
Monads solve the problem of the composition of Kleisli arrows, which are
functions of type (A) -> M<B> .
Kleisli arrows are how you model functions from a type A to an embellished
version of a type B you represent as M<B> .
The embellishment of a type M<A> is a way to encapsulate effects in the
return type of a function.
Monads are functors and provide a map function following the functor laws.
The bind operator, >>= , is a way to solve the type impedance between a
function returning a value of type M<B> and a function accepting a value of
type B in input.
The flatten function allows you to simplify the way you implement bind
in the case of functors. It has type (M<M<A>>) -> M<A> .
You can implement flatMap using fish , bind and flatten .
Monads are the way you compose functions encapsulating side effects in the
result type.
327
Functional Programming in Kotlin by Tutorials
In the first two sections of this book, you learned everything you need to know
about pure functions. You learned that a pure function has a body that’s
referentially transparent and, maybe more importantly, doesn’t have side
effects. A side effect is a “disturbance” of the world external to the function,
making the function difficult to test. Exceptions are a very common example of
a side effect. A function containing code that throws an exception isn’t pure and
must be fixed if you want to use all the concepts you’ve learned so far.
In this chapter, you’ll learn how to handle exceptions — and errors in general —
using a functional programming approach. You’ll start with a very simple
example before exploring the solution Kotlin provides using the Result<T>
data type. In particular, you’ll learn:
What the Kotlin Result<T> data type is and how to use it in your application.
You’ll learn all this with a practical example that allows you to fetch and parse
some content from the internet. This is the same code you’ll use in the RayTV
app, which allows you to access data about your favorite TV shows.
Note: You’ll use the RayTV app in the following chapters as well.
You’ll also have the chance for some fun with a few exercises.
Exception handling
328
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
Note: Feel free to skip this section if you already have a clear understanding
of what exceptions are and why they exist.
Every application is the implementation of a set of use cases in code. A use case
is a way you describe how a specific user can interact with a system. Figure 14.1
is an example of a use case describing the scenario when a RayTV app user
wants to search for their favorite TV show.
1. Who can access the specific feature of the app. In this case, it’s the RayTV app
user.
2. What the RayTV app can do. In this case, it allows the user to search for a TV
show by name.
Usually, you also describe the use case in words by writing a document like this:
It describes what the user can do and how the system reacts. This use case is
very specific, though. It describes when everything works fine. You call this the
happy path or principal use case. Many things can go wrong. For instance, a
network error can happen, or the search simply doesn’t produce any results. In
this case, you describe these situations using alternative use cases, like the
one in Figure 14.2:
329
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
In this case, you describe the scenario as a use case that shows what happens
when the happy path isn’t followed. In this case, the search doesn’t produce any
result. Usually, you describe this scenario with a document like this:
This is all good, but what do use cases have to do with errors and exceptions?
Well, exceptions aren’t always bad things. They’re just a way programming
languages represent alternative scenarios. In general, you have three different
types of situations:
Errors: These represent cases when something really wrong happens. They
aren’t recoverable, and the developer should understand the cause and solve
it. Typical examples are VirtualMachineException , IOError or
AssertionError .
330
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
type of exception because it can happen at runtime. A typical one is
NullPointerException , which is thrown when you access a member of an
object through a null reference. Although you can recover from a
RuntimeException , they usually describe bugs you should eventually fix.
Kotlin doesn’t have checked exceptions, which means you don’t need to declare
them as part of the function throwing them. This doesn’t mean you shouldn’t
handle the exception. Kotlin represents them with classes extending
Throwable and still provides you the try-catch-finally expression. So
what’s the best way to handle exceptions controlling the flow of your program?
Now that you have a solid functional programming background, you know that
exceptions are side effects. How can you use what you’ve learned to handle
exceptions elegantly and efficiently? Monads, of course! :]
Note: It’s interesting to remember how the type of the following expression is
Nothing :
Remembering the analogy between types and sets, you know that Nothing is
a subtype of any other type. This means the value you return when you throw
an exception has a type compatible with the return type of the function itself.
1. Fail fast: Stopping the execution flow of your program and returning the
first error that happens. You use this approach when you can’t proceed
because you need data that you failed to get in a previous step.
2. Collection errors: Proceeding in your program flow, collecting all the errors
and making them part of the final result. Validation of some data is a classic
example. An address, for instance, can be wrong for many different reasons,
and you’d like to collect them all.
331
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
1. Optional<T> or Either<E, T> for failing fast.
2. Applicative functors for collection errors.
It’s now helpful to see both of them using some practical examples.
Some preparation
Before describing the different ways of handling exceptions, it’s useful to look at
some of the classes you find in the material for this chapter. These are the
classes you initially find in the fp_kotlin_cap14 project, and you’ll see them
again in the RayTV app in their final state. The RayTV app will allow you to
search for a TV show and display some of the show’s information on the screen.
This is basically the use case in Figure 14.1.
Note: For the RayTV app, you’ll use a simple API from TVmaze, which doesn’t
require a key or registration.
Parsing the JSON to get the information you need encapsulated into a model
you find in the model sub-package.
Note: All the sub-packages are related to the main package for the
application, which is com.raywenderlich.fp.
In TvShowFetcher.kt, you have the code to send a request to the server, which
returns a String .
object TvShowFetcher {
fun fetch(query: String): String {
val encodedUrl = java.net.URLEncoder.encode(query, "utf-8")
val localUrl =
URL("https://api.tvmaze.com/search/shows?q=$encodedUrl")
with(localUrl.openConnection() as HttpURLConnection) {
requestMethod = "GET"
val reader = inputStream.bufferedReader()
return reader.lines().toArray().asSequence()
.fold(StringBuilder()) { builder, line ->
builder.append(line)
}.toString()
}
}
}
332
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
In TvShowParser.kt, you have the code that parses the JSON, returning a list of
ScoredShow , which is the class you use to describe each result.
object TvShowParser {
/** Parses the input json */
fun parse(json: String): List<ScoredShow> = Json {
ignoreUnknownKeys = true
}.decodeFromString<List<ScoredShow>>(
ListSerializer(ScoredShow.serializer()), json
)
}
To prove that, run some unit tests you find where shown in Figure 14.3:
333
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
Of course, TvShowFetcher makes a network request, which can fail for many
reasons, like a simple missing connection. The TvShowParser can fail simply by
getting an incorrect JSON input. But how can you handle these exceptions?
fun fetchTvShowOptional(
query: String
): Optional<String> = try { // 1
Optional.lift(TvShowFetcher.fetch(query))
} catch (ioe: IOException) {
Optional.empty()
}
Both use the try-catch expression and the Optional<T> type you find in lib.
This is exactly what you learned in Chapter 13, “Understanding Monads”, where
you implemented flatten and bind to basically create flatMap .
334
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
fun main() {
fetchAndParseTvShow("Big Bang Theory") // 1
.getOrDefault(emptyList<ScoredShow>()) pipe ::println // 2
}
Here, you:
335
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
This is quite good because it allows you to test each function in isolation and
then trust the composition between two functions in the Kleisli category, as you
learned in Chapter 13, “Understanding Monads”. However, you can do much
better. Disconnect your computer and run the code again, and you’ll get the
following output:
[]
Is this an error, or is this the actual result coming from the server? With the
previous code, you’d never know. You need a way to get more information about
the error. Regardless, a good point about this solution is that you don’t invoke
parseTvShowString at all if the fetchAndParseTvShow fails.
fun fetchTvShowEither(
query: String
): Either<IOException, String> = try {
Either.right(TvShowFetcher.fetch(query)) // 1
} catch (ioe: IOException) {
Either.left(ioe)
}
This case is somewhat similar to Optional<T> , but using Either<E, T> . Here,
you define:
336
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
2. parseTvShowEither , which invokes TvShowParser::parse and returns the
result as Either.Right in the case of success and the exception in an
Either.Left in the case of an error. In this case, the return type is
Either<SerializationException, List<ScoredShow>> .
As you did with Optional<T> , you use flatMap to create the following, which
you add to the same file:
fun main() {
fetchAndParseTvShowEither("Big Bang Theory")
.leftMap {
println("Error: $it")
}
.rightMap {
println("Result: $it")
}
}
Getting:
337
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
url=https://www.tvmaze.com/shows/66/the-big-bang-theory,
image=ShowImage(original=https://static.tvmaze.com/uploads/images/o
riginal_untouched/173/433868.jpg,
medium=https://static.tvmaze.com/uploads/images/medium_portrait/173
/433868.jpg), summary=<p><b>The Big Bang Theory</b> is a comedy...
</p>, language=English))]
The interesting part happens when you have some errors. Disconnect your
computer from the network and run the code again. You’ll get something like:
You’ll get:
Error: kotlinx.serialization.json.internal.JsonDecodingException:
Unexpected JSON token at offset 1589: Expected EOF after parsing,
but had s instead
JSON input: .....ze.com/episodes/1646220}}}}]sabotage
As you see, the first exception that happens is the one you’ll get as output. This
is also true because you can’t parse JSON you don’t have in the case of a
fetching error. In some cases, you want to collect all the exceptions that happen,
which is a classic use of the applicative functor.
Applicative functor
In the previous section, you learned how to handle exceptions using
Optional<T> and Either<E, T> data types following a fail fast approach that
stops the execution flow at the first exception. With Either<E, T> , you also get
what’s wrong. In Chapter 9, “Data Types”, you learned that Either<A, B> is a
bifunctor, which is an algebraic data type representing the addition of two
types, A and B . In the previous use case, you used Left<E> to represent the
error case and Right<T> for the success case.
In the context of error handling, you usually create a dedicated data type you
338
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
call Result<E, T> .
Note: As you’ll learn later, Kotlin provides a built-in Result<T> data type,
which is different from the one you’ll create in this section. To avoid conflicts,
you’ll call yours ResultAp<E, T> , where the Ap suffix represents its
applicative behavior.
As a first step, open ResultAp.kt in applicative and add the following code:
companion object {
@JvmStatic
fun <E : Throwable> error(
error: E
): ResultAp<E, Nothing> = Error(error) // 2
@JvmStatic
fun <T> success(
value: T
): ResultAp<Nothing, T> = Success(value) // 2
}
}
ResultAp<E, T> is very similar to Either<E, T> and, besides the name, it
differs in that:
1. Parameter type E has Throwable as its upper bound. This allows you to
just use exceptions for the type E , but you can also remove that limitation if
you prefer.
2. Factory methods are now called error and success to make their
339
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
meaning explicit.
Exercise 14.1: ResultAp<E, T> is very similar to Either<E, T> . Can you
implement flatMap for it as well?
Yeah, it’s true. With ResultAp<E, T> , you haven’t done much yet, but now
comes the fun!
In this code, ap :
2. Accepts a parameter of type ResultAp<E, (T) -> R> where the value in the
case of success is a function (T) -> R .
3. Returns ResultAp<E, R> .
This is basically a way to apply a function to a value only in the case of success,
as you see in the implementation you add in ResultAp.kt in applicative:
340
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
is Error<E> -> when (this) {
is Success<T> -> Error(fn.error)
is Error<E> -> Error(this.error)
}
}
Cool. Now you understand what it is, but how can ap be useful in the context
of exception handling? A typical case involves validation.
Open Validation.kt in validation and add the following User data class.
Now, imagine you want to create a User starting with some values you enter
from a UI, and those values require some sort of validation. To simulate that,
add this code to the same file:
341
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
2. validateName as a function that validates the name property.
Now comes the magic! Just add this to the same file:
fun main() {
val userBuilder = ::User.curry() // 1
val userApplicative = ResultAp.success(userBuilder) // 2
val idAp = ResultAp.success(1) // 3
validateEmail("max@maxcarli.it") // 6
.ap(
validateName("") // 5
.ap(
idAp.ap(userApplicative) // 4
)
)
.errorMap { // 7
println("Error: $it"); it
}
.successMap { // 7
println("Success $it")
}
}
5. You now pass the value you got from the previous point to ap of the
Result<ValidationException, String> you get from validateName . This
swallows another parameter, and you get a ResultAp<ValidationException,
(String) -> User> .
342
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
String> you get from validateEmail and get a
Result<ValidationException, User> , which is the final result.
Just apply this change to add a name and run the code again:
fun main() {
// ...
validateEmail("max@maxcarli.it")
.ap(
validateName("Massimo") // HERE
.ap(idAp.ap(userApplicative))
)
// ...
}
You’ll get:
This looks good, but you still have two problems to solve:
2. If you enter an invalid email and an invalid name, you only get the error
about the former. It would be nice to know about both.
You can solve the first problem by adding the following code that creates appl
as an infix version of ap :
fun main() {
val userBuilder = ::User.curry()
343
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
val userApplicative = ResultAp.success(userBuilder)
val idAp = ResultAp.success(1)
(userApplicative appl
idAp appl
validateName("Massimo") appl
validateEmail("max@maxcarli.it"))
.errorMap {
println("Error: $it"); it
}
.successMap {
println("Success $it")
}
}
Now, appl allows you to follow the same order for validation of the parameters
in ::User . The only problem is a limitation of Kotlin that doesn’t allow you to
set the precedence between the operators, forcing you to use parentheses
before errorMap and successMap .
The second problem gives you another opportunity to use a concept you
learned about in previous chapters: semigroups.
fun main() {
val userBuilder = ::User.curry()
val userApplicative = ResultAp.success(userBuilder)
val idAp = ResultAp.success(1)
(userApplicative appl
idAp appl
validateName("") appl // HERE
validateEmail("")) // HERE
.errorMap {
println("Error: $it"); it
}
.successMap {
println("Success $it")
}
}
344
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
Error: com.raywenderlich.fp.validation.ValidationException: Invalid
email
This is correct, but you can do better. Both name and email are invalid, but
the error message has no mention of the former. You need to find a way to
somehow accumulate the errors into one. In Chapter 12, “Monoids &
Semigroups”, you learned that a monoid describes a way to combine two values
into a single value of the same type. You can represent the properties of a
monoid in different ways. For example, in ValidationSemigroup.kt, add the
following definition:
interface Semigroup<T> {
operator fun plus(rh: T): T
}
The Semigroup<T> interface here defines types with the plus operator. Now,
you can create a different type of ValidationException , which is also a
Semigroup , like this:
The next step now is to implement a version of ap , you call apsg , that handles
Semigroup s. In ValidationSemigroup.kt add the following code:
345
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
1. The type parameter E has two upper bounds. It must be a Throwable and a
Semigroup<E> .
As you did previously for ap , you can provide an infix version by adding this
code:
fun validateNameSg(
name: String
): ResultAp<ValidationExceptionComposite, String> =
if (name.length > 4) {
Success(name)
} else {
Error(ValidationExceptionComposite(
listOf(ValidationException("Invalid name"))
))
}
fun validateEmailSg(
email: String
): ResultAp<ValidationExceptionComposite, String> =
if (email.contains("@")) {
Success(email)
} else {
Error(ValidationExceptionComposite(
listOf(ValidationException("Invalid email"))
))
}
346
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
Note how you now use ValidationExceptionComposite , which contains a list
of ValidationException .
Finally, you can replace the main and use the new operators like this:
fun main() {
val userBuilder = ::User.curry()
val userApplicative = ResultAp.success(userBuilder)
val idAp = ResultAp.success(1)
(userApplicative applsg
idAp applsg
validateNameSg("") applsg
validateEmailSg(""))
.errorMap {
println(it.localizedMessage); it
}.successMap {
println("Success $it")
}
}
Invalid name
Note: If you want to learn all about the Result<T> API, the Kotlin Apprentice
book is the right place to go.
Looking at the source code for Result<T> , you’ll notice that it’s not
347
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
implemented as a sealed class. Result<T> has a single parameter type T , but
the actual internal value has type Any? . This is because Result<T> handles
the Failure case, assigning an instance of the internal class Result.Failure
to value .
The goal here is to see if Result<T> is a functor first and then a monad.
Result<T> as a functor
To prove that Result<T> is a functor, you should verify the functor laws, and in
particular, that:
1. map id == id
First, you see that Result<T> APIs have map with the following
implementation:
@InlineOnly
@SinceKotlin("1.3")
public inline fun <R, T> Result<T>.map(
transform: (value: T) -> R
): Result<R> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when {
isSuccess -> Result.success(transform(value as T))
else -> Result(value)
}
}
Removing the contract part and replacing transform with id , you get:
348
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
public inline fun <R, T> Result<T>.map(): Result<R> =
Result.success(value)
Considering that the value in the case of failure is the Result.Failure instance
itself, this completes the proof of the first functor law.
Proving the second law can be more verbose, but you can actually make it
shorter by noting that the function transform you pass as a parameter is only
used in the case of success. If you have a success, Result<T> , and map a
function f first and then a function g , you’ll get the same value you’d get
map ping f compose g .
Another, more pragmatic and quick proof that Result<T> is a functor is the
presence of the map in the APIs. Looking at the same APIs, you can’t find a
flatMap function, which makes you wonder if the Result<T> data type is a
monad or not.
Result<T> as a monad
As mentioned at the end of the previous section, the Kotlin Result<T> data
type doesn’t have a flatMap . What happens, then, if you need to compose a
function of type (A) -> Result<B> with a function of type (B) ->
Result<C> ? Well, in Chapter 13, “Understanding Monads”, you learned how to
handle this case with a generic data type M<A> . It’s time to do the same for
Result<T> .
In this code, you simply replaced M<T> with Result<T> in the definition of the
operators:
349
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
1. fish
2. bind
Here, you:
2. Return its value in the case of success. Note that the value of
Result<Result<A>> has type Result<A> .
3. Return what you get from Result::failure , passing the exception in the
case of failure.
Now, you can add the following code to the same file:
350
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
Result.failure(ioe) // 2
}
Here, you:
fun main() {
fetchAndParseTvShowResult("Big Bang Theory")
.fold(onFailure = {
println("Error: $it")
}, onSuccess = {
println("Result: $it")
})
}
351
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
As you did before, run again with your computer disconnected from the
network, getting:
Error: kotlinx.serialization.json.internal.JsonDecodingException:
Unexpected JSON token at offset 1589: Expected EOF after parsing,
but had s instead
JSON input: .....ze.com/episodes/1646220}}}}]sabotage
Using Android Studio, open the RayTV project in the material for this chapter.
When you run the project, after the splash screen, you’ll get what’s in Figure
14.4:
Enter some text in the TextField at the top of the screen, and you’ll get some
results like the following:
When you select a TV show from the list, you see its details, as in Figure 14.6:
353
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
You’ll play with the RayTV app more in the following chapters. In this case, it’s
interesting to look at how it handles errors.
These are the same functions you implemented in the first part of the chapter.
354
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
@HiltViewModel
class SearchViewModel @Inject constructor() : ViewModel() {
Besides some code related to the use of Jetpack Compose, Coroutines and Hilt,
you can note the following:
3. You use fold and change the state to FailureSearchResult in the case of
failure, encapsulating the Throwable .
4. In the case of success, you set the state to SuccessSearchResult ,
encapsulating the result.
5. Here, you also use a special case if the query is successful but you don’t get
any result.
Note: If you want to learn everything you need to know about coroutines, the
Kotlin Coroutines by Tutorials book is the right place to go. Jetpack Compose by
355
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
Tutorials is the best resource for learning how to create UI using Compose.
With Dagger by Tutorials, you’ll learn everything you need to know about
Dagger and Hilt. Finally, Real World Android by Tutorials will help you
understand the best way to put all these technologies together.
// ...
ErrorAlert(errorMessage = {
stringResource(R.string.error_message)
}) {
result is FailureSearchResult
}
// ...
Key points
Error handling is a fundamental part of any software application.
Many programming languages model errors using exceptions.
You can use Optional<T> and Either<E, T> to model functions throwing
356
Chapter 14: Error Handling
Functional Programming in Kotlin by Tutorials With Functional Programming
exceptions.
You can make Result<T> a monad by following the same process you used
in Chapter 13, “Understanding Monads”.
357
Functional Programming in Kotlin by Tutorials
15 Managing State
Written by Massimo Carli
In Section II, you learned about the concept of data type using the analogy of a
container providing its content some context. For instance, Optional<A>
represents a box that can either be empty or contain a value of type A . The
context of a data type is important when you make it a functor or a monad. In
the case of a functor, you can apply to Optional<A> a function Fun<A, B> and
get an Optional<B> . If the initial Optional<A> is empty, you’ll get an empty
Optional<B> . If Optional<A> contains a value of type A , you apply the
function Fun<A, B> , to get a value of type B wrapped in an Optional<B> . If
the function you apply is of type Fun<A, Optional<B>> , you give Optional<A>
the superpower of a monad and use flatMap to get an Optional<B> whose
content depends on whether the value A is present. Different data types
provide different contexts and behave differently as functors and monads.
The question now is: Can you create a data type representing a box that
encapsulates some data and the effect that happens when you apply functions
with map or flatMap to the same data? With this data type, you wouldn’t
prevent the change of some state, but you’d be able to control it.
The solution is the State<T> data type, which is the topic of this chapter. Here,
you’ll learn:
358
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
What the State<S, T> monad is.
This is probably one of those chapters you’ll need to read multiple times, but it’ll
definitely be worth it!
The problem
To describe how the State<S, T> data type works, it’s helpful to start with a
very simple example. You can follow along with it in the Inventory.kt file in the
material for this project. Start by adding this code:
Note: A stock keeping unit (SKU) is a scannable bar code that you usually find
printed on product labels in retail stores. It’s used to track inventory
movement automatically.
In this case, suppose the SKU has the format RAY-PROD-#### , where ####
represents a four-digit number that must be unique for each product.
Note: Please ignore the fact that, with this format, you can only have 10,000
different SKUs. This just allows the code to remain simple so you can focus on
the state. Of course, you can implement your SKU generator algorithm
however you want.
var count = 0 // 1
359
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
In this code, you:
To test the previous function, add and run the following code:
fun main() {
val prod1 = Product("1", "Cheese")
val prod2 = Product("2", "Bread")
val prod3 = Product("3", "Cake")
Getting in output:
Everything looks fine, but it’s actually not! Now that you know all about pure
functions and side effects, you surely noted that createSku isn’t pure — every
time you invoke it, you change the state of the universe that, in this case, you
represent with count .
State transformation
createSku is impure because of the side effect related to the count update,
which happens every time you invoke createSku . This creates the SKU based
on the current value of count , which is the current state. createSku also
updates the state, preparing for the next invocation. You can generalize this
behavior with the following definition, which you can add to State.kt:
360
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
This is because you replaced S with Int , and the transformation consists of
adding 1 to the current state.
In your example, you need to use the state to create a new SKU and use
skuStateTransformer to update the state. A better abstraction to handle this
behavior is the following:
Note: The different order for the type parameters S and T in the definition
and the returning Pair<T, S> might introduce some confusion. The
important thing is to be consistent in the code that follows.
This is the abstraction for all the functions receiving a state of type S as input,
and returning both a value of type T and the new state of type S . The
returned T might depend on the current state.
fun main() {
val prod1 = Product("1", "Cheese")
val prod2 = Product("2", "Bread")
val prod3 = Product("3", "Cake")
val state0 = 0 // 1
val (sku1, state1) = skuStateTransformer(state0) // 2
361
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
SkuProduct(prod1, sku1) pipe ::println // 3
val (sku2, state2) = skuStateTransformer(state1) // 4
SkuProduct(prod2, sku2) pipe ::println // 5
val (sku3, state3) = skuStateTransformer(state2) // 6
SkuProduct(prod3, sku3) pipe ::println // 7
}
Looking at this code, you can’t say you made everything easier to use. This is
because you have to:
3. Pass the new state to another StateTransformer<S, T> to get the next state.
4. Keep going until you get all the type T values you need.
You should find a way to make all these “passing values” for the state automatic
and somehow hidden under the hood. This is what you’ll achieve in the
following paragraphs, and this is what the state monad is for.
362
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
you need to start from a Product and get a SkuProduct .
To prove this, add the following code in Inventory.kt, and see that it compiles:
val curriedAssignSku:
(Product) -> StateTransformer<Int, SkuProduct> =
assignSku.curry()
Product SkuProduct
assignSku
Int Int
You can see that assignSku has a visible part that maps a Product into a
SkuProduct and an invisible one that’s responsible for updating the state.
fun main() {
val prod1 = Product("1", "Cheese")
val prod2 = Product("2", "Bread")
val prod3 = Product("3", "Cake")
val state0 = 0
val (skuProd1, state1) = curriedAssignSku(prod1)(state0)
skuProd1 pipe ::println
val (skuProd2, state2) = curriedAssignSku(prod2)(state1)
skuProd2 pipe ::println
val (skuProd3, state3) = curriedAssignSku(prod3)(state2)
skuProd3 pipe ::println
}
You can already see some minor improvements, but you still want to remove the
need to pass the current state.
If you want to think of this in terms of containers again, you can look at the
364
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
State<S, T> as a box whose context is about the encapsulation of a
StateTransformer<S, T> that describes how the state changes at every action
you do through the box itself.
To make things a little bit easier and remove the effort due to the definition of
the State<S, T> data type, add the following code:
In this case, you can start from a State<S, T> and use the invoke function or
the () , pass the current state, and apply to it the StateTransformer<S, T> it
encapsulates. Just remember that the type for invoke is (State<S, T>) ->
(S) -> Pair<T, S> .
Now that you have the State<S, T> data type, you need to add some magic
starting from lift up to the monadic superpower. Now, the real fun starts. :]
Implementing lift
The first operation you need to implement is lift , also called return in
other languages. This is the function you use to get a value of type T and put it
into the box related to the specific data type, in this case, State<S, T> . In
State.kt, change the State<S, T> definition like this:
companion object { // 1
@JvmStatic
fun <S, T> lift(
value: T // 2
): State<S, T> = // 3
State({ state -> value to state }) // 4
}
}
With lift , you can start from a simple value and get a State<S, T> for it, like
365
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
this:
fun main() {
val initialState = State.lift<Int, Product>(Product("1",
"Cheese"))
}
Just note how you need to help the Kotlin type inference by providing the type
for the input type parameters S and T . This is because Kotlin infers T ‘s type
from the value you pass, but it can’t understand S ’s type on its own.
Before writing the code, take some time to think about what it means for a
State<S, A> to be a functor. You start with a value of type A and, using map ,
you apply a function of type Fun<A, B> , getting a value of type B . The
State<S, A> needs to handle the state update. To understand how it works,
add the following code to the StateFunctor.kt file:
5. To get the new state and the value of type A , you need to invoke the state
transformer of type StateTransformer<S, A> encapsulated in the receiver.
Remember that you can do this because you defined the invoke operator
on the State<S, T> type above. Another option would be simply to invoke
st(state) .
366
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
6. Invoke the input parameter fn , passing the value of type A you got in the
previous step, and get the value you return along with the new state.
In the previous code, it’s essential to understand that the new state you get with
State<S, B> is the same as what you get with the initial State<S, A> . The
difference is that the value of type B in State<S, B> is the one you get
applying Fun<A, B> to the value of type A in State<S, A> .
fun main() { // 4
skuState(0) pipe ::println
skuSerialState(0) pipe ::println
}
1. skuSerial , which has the type Fun<String, String> and returns the last
4 characters of the input String .
4. main that prints the result of skuState and skuSerialState passing the
same value as the initial state.
(RAY-PROD-0000, 1)
(0000, 1)
As you see, the new state is the same, but skuSerial has been applied to the
value of type String .
367
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
Note: In the previous code, you considered up to four input parameters, but,
of course, you could consider all the cases up to N.
If you want to generalize map for functions of different input parameters, you
could just provide an implementation of map for each of those. For instance, in
the case of Fun2<A, B, C> , you could define the following:
Here, you:
368
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
passing the two parameters together, like fn(pair.first, pair.second) .
With Fun2<A, B, C> , this isn’t a problem, but imagine if you had to implement
all these map versions for all the possible options. This would be very tedious.
Fortunately, this is the case where the applicative functor typeclass comes into
play. Similar to what you saw in Chapter 14, “Error Handling With Functional
Programming”, to define all possible map s for all the possible functions with
multiple parameters, you just need two basic functions you’ve already met. The
first is your lift that, in the context of an applicative functor, is called pure .
The second is the ap function that, in the context of the State<S, T> data
type, has the following signature:
As you can see, ap is an extension function for the type State<S, T> and
accepts in input a State<S, (T) -> R> where the value is actually a function
from T to R . The result, then, is a State<S, R> .
Before implementing its body, it’s interesting to see how you can use ap to
handle functions of multiple parameters. You do this using the applicative
style.
Suppose you want to apply a function with three parameters of type Fun3<A, B,
C, R> , which is equivalent to a Chain<A, B, C, R> typealias of (A) -> (B) -
> (C) -> R . As a simple example, add the following code to
StateApplicative.kt:
fun replaceSuffix(
input: String,
lastToRemove: Int,
postfix: String
) = input.dropLast(lastToRemove) + postfix
You can make it of type Chain<String, Int, String, String> using curry ,
like this:
369
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
infix fun <S, A, B> State<S, (A) -> B>.appl(a: State<S, A>) =
a.ap(this) // 1
fun main() {
val initialStateApp = State
.lift<Int, Chain3<String, Int, String, String>>(
cReplaceSuffix
) // 2
val inputApp = State.lift<Int, String>("1234567890") // 3
val lastToRemoveApp = State.lift<Int, Int>(4) // 3
val postfixApp = State.lift<Int, String>("New") // 3
val finalStateApp = initialStateApp appl
inputApp appl lastToRemoveApp appl postfixApp // 4
1. Define appl for State<S, A> , the same way you did in Chapter 14, “Error
Handling With Functional Programming”, to make the code more readable.
Of course, at the moment, you can’t run this code because you still need to
implement app .
370
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
4. Invoke the State<S, R> constructor, passing a lambda with the current
state s0 as the initial state.
5. Get the value of type T and the new state s1 , invoking the current receiver
with the initial state.
6. Use the new state s1 as input for fn to get the value of type (T) -> R and
the final state s2 .
7. Get the final result, invoking fn on the value of type T you got in Step 5
and using the final state s2 .
Note how you get the values from the current receiver first and then from the
input State<S, (T) -> R> . Also, note how the state updates twice. The first
update is based on the current receiver of type State<S, T> , and the second is
because of the one of input State<S, (T) -> R> .
(1234567890, 0)
(123456New, 0)
As you can see, you applied replaceSuffix to the input String , but the value
for the state of type Int remained the same. This is actually expected because
this works exactly like the functor. It just gives you the chance to apply
functions with multiple parameters to the value of type T in State<S, T> ,
leaving the state unchanged.
371
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
Here, you:
1. Define flatMap as an extension function for the State<S, A> data type.
So, if the State<S, A> data type is a way to encapsulate some state and the
logic to update it, flatMap is the tool you use to compose a function of type
(A) -> State<S, B> coming from the Kleisli category.
You’ll probably be surprised at how simple the flatMap implementation is. Just
replace the previous definition with the following code:
Here, you:
As a simple example of how to use State<S, T> as a monad, add the following
code to the same StateMonad.kt file:
val assignSkuWithState: // 1
(Product) -> State<Int, SkuProduct> =
{ prod: Product ->
State(curriedAssignSku(prod)) // 2
}
fun main() {
val prod1 = Product("1", "First Product") // 3
val initialState = State.lift<Int, Product>(prod1) // 4
val finalState = initialState.flatMap(assignSkuWithState) // 5
finalState(0) pipe ::println // 6
}
372
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
4. Use lift to define the State<Int, Product> from the value prod1 of
type Product . This is the initial state, initialState .
6. Invoke finalState , passing the initial state value 0 , and send the result to
the standard output.
The initial value for the state was 0 , and this is the one assignSku used to
generate the SKU RAY-PROD-0000 . The new value for the state is 1 . This is
because lift doesn’t apply the state transformation but returns a value for
the state that’s the same value you have in input.
A practical example
In the previous example, you didn’t have the chance to appreciate the hidden
state transformation that the State<S, T> monad does for you behind the
scenes. As a more complicated example, suppose you have an FList<Product> ,
and you want to assign each one a unique value for the SKU, getting an
FList<SkuProduct> as output.
373
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
Product("3", "Cake"),
Product("4", "Pizza"),
Product("5", "Water")
)
This is a simple FList<Product> with some dummy data, and you want to get
an FList<SkuProduct> , assigning each one a unique SKU value.
A possible solution implies the use of the map function, like this:
var currentCount = 0
fun inventoryMap(products: FList<Product>): FList<SkuProduct> {
return products.map {
SkuProduct(it,
"RAY-PROD-${String.format("%04d", currentCount++)}")
}
}
fun main() {
inventoryMap(products).forEach(::println)
}
Getting:
But, there’s a but! inventoryMap isn’t pure because it uses and changes
currentCount , which is part of the external world. A possible solution could be
to move currentCount inside inventoryMap , like this:
fun inventoryMapWithCount(
products: FList<Product>
): FList<SkuProduct> {
var internalCount = 0
return products.map {
SkuProduct(it,
"RAY-PROD-${String.format("%04d", internalCount++)}")
}
}
The output would be the same. internalCount now has the scope
inventoryMapWithCount , and it’s not considered as a side effect. The problem
now is that internalCount starts from 0 every time you invoke
374
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
inventoryMapWithCount , so it’ll produce duplicated SKUs.
In this chapter, you learned that a possible solution is to add the current state as
part of the input parameter and define a listInventoryHelper function, like
the following:
fun listInventory(
products: FList<Product>
): (Int) -> Pair<Int, FList<SkuProduct>> =
when (products) { // 1
is Nil -> { count: Int -> count to Nil } // 2
is FCons<Product> -> { count: Int -> // 3
val (newState, tailInventory) =
listInventory(products.tail)(count)
val sku = "RAY-PROD-${String.format("%04d", newState)}"
newState + 1 to FCons(
SkuProduct(products.head, sku), tailInventory)
}
}
In this code:
3. If you have an FCons<Product> , you need to handle the head first, getting a
SKU and creating the related SkuProduct . For the tail, you just need to
invoke listInventory recursively.
fun main() {
listInventory(products)(0).second.forEach(::println)
}
Getting:
This approach works, but you can improve it. The main issues are:
1. The SKUs are assigned in reverse order. This might not be a problem as long
as they’re unique.
2. You need to handle the internal state explicitly. This is error-prone and
375
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
Fortunately, the State<S, T> monad helps. To understand how, you need to
create a utility function first. Add the following code to State.kt:
4. Return a State<S, C> whose value is the value of type C you get by
invoking combine on the values of types A and B .
5. Create the result, invoking the State<S, C> constructor.
6. Invoke the receiver with the initial state, getting the value of type A and the
new state.
7. Use the new state to get the value of type B and an updated version of the
state.
8. Get the value of type C , invoking combine , and return the result along with
the last version of the state.
fun inventory(
list: FList<Product>
): State<Int, FList<SkuProduct>> = // 2
when (list) { // 3
is Nil -> State.lift(Nil) // 4
is FCons<Product> -> {
val head = State.lift<Int, Product>(list.head) // 5
.flatMap(addSku)
376
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
val tail = inventory(list.tail) // 6
head.zip(tail) { a: SkuProduct, b: FList<SkuProduct> -> // 7
FCons(a, b)
}
}
}
Here, you:
4. If it’s a Nil , you just return the same encapsulated into a State<Int,
FList<SkuProduct>> using lift .
5. If it’s an FCons<Product> , you handle the head first. First, you use lift to
get a State<Int, Product> , and then use flatMap with addSku to get a
State<Int, SkuProduct> .
6. Invoke inventory recursively on the tail to get the related state of type
State<Int, FList<SkuProduct>> .
7. Use zip to combine the State<Int, FList<SkuProduct>> for the head and
the tail in a single FList you return as a result.
To test how this works, add and run the following code:
fun main() {
inventory(products)(0).first.forEach(::println)
}
Getting as output:
As you can see, now the SKUs have the right order, but, more importantly, all
the state management is handled entirely under the hood. In the last version of
inventory , you don’t pass over any state, and the code is completely
functional.
377
Functional Programming in Kotlin by Tutorials Chapter 15: Managing State
Key points
A data type is like a container that provides some context to its content.
The context of a data type impacts how you interact with its content when
applying some functions.
The State<S, T> data type encapsulates the concept of state transition.
You can make State<S, T> a functor providing the implementation for
map .
map on a State<S, T> applies a function to the value of type T but leaves
the state unchanged.
You can make State<S, T> a monad, providing implementation for the
flatMap .
The State<S, T> allows you to define two different types of transactions.
The first, on the value, is visible. The second, on the state transition, is
hidden.
378
Functional Programming in Kotlin by Tutorials
In Chapter 15, “Managing State”, you implemented the State<S, T> data type
and gave it the superpowers of a functor, applicative and monad. You learned
that State<S, T> describes the context of some state of type S that changes
based on some transformations you apply every time you interact with the value
of type T in it. Making the State<S, T> a monad means you can compose
different functions of type (A) -> State<S, B> . The State<S, B> data type is
just a way to encapsulate a StateTransformer<S, T> . This means you can
compose functions of type (A) -> StateTransformer<S, B> that’s basically a
function of type (A) -> (S) -> Pair<S, B> . If you use uncurry , this is
equivalent to a function of type Pair<A, S> -> Pair<S, B> .
Now, think about what an impure function is. It’s a function whose body is an
expression that is not referentially transparent because, when executed, it
changes the state of the world outside the function body. This means it has a
side effect, which breaks composition. But if a side effect is a change in the
state of the world, the question now is: Can you somehow represent the state of the
world as the type S you use in a State<S, T> and encapsulate the side effect as a
simple transformation? In other words, if you define the type World as
representing the current state of the world, can you use State<World, T> as a
data type that encapsulates any possible side effects?
The answer to this question is yes, and the specific data type is IO<T> .
The app you’ll implement here is a little bit different because it’ll allow you to
read a name from the standard input and then print a greeting message. Open
Greetings.kt in the material for this chapter, and write the following code:
fun main() {
print("What's your name? ") // 1
val name = Scanner(System.`in`).nextLine() // 2
print("Hello $name\n") // 3
}
2. Use Scanner to read the name you type as input and save it to name .
3. Use name to format and print a greeting message.
Feel free to run it, and, after entering your name, you’ll get an output like the
one in Figure 16.1:
Note: When you run the app, just put the cursor after the input message to
insert your name, as shown in Figure 16.1. Then, type your name and press
Enter.
The previous code works very well, but the expression in main is anything but
pure. Using Scanner , you read the name from the standard input. Using
print , you display the result on the standard output. They’re both side effects:
interaction with the rest of the world. So, how can you create the previous
program but handle side effects in a pure and functional way?
The introduction of this chapter already gave you a hint. What if you think of
the external world as a giant state you change when you read the name and
write the greeting message?
380
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
You can follow this idea starting with the definition of a type you call World .
Add the following in the World.kt file:
Here, you define World as a simple alias for the Unit type. At this point, how
you define the World type doesn’t really matter. You’ll see later if how you
define World really matters or not. In the same file, add the following:
To prove that you can modify the initial program as the composition of the
function, you use:
printString ‘s type is a little more interesting. It’s (String, World) -> World
because it receives the String to print and the current World in input,
returning the new state for the World . In this case, you have two input
parameters, but you can apply curry , getting the type (String) -> (World) -
> World . With the previous definition of SideEffect , you can say that the type
of printString is (String) -> SideEffect . In this way, you make the
definition more explicit. Then, add the following code to the same Greetings.kt
file:
381
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
val printString: (String) -> SideEffect = { str: String ->
{ a: World ->
print(str) to World
}
}
Note: As you’ll see later, a type like (String) -> SideEffect says something
crucial. It says that printString doesn’t execute a side effect but returns a
description of it. This is the main reason it’s a pure function now.
Now, test each of the previous functions by running the following code:
fun main() {
// ...
readName(World) pipe ::println // 1
printString("Hello Max \n")(World) pipe ::println // 2
}
1. readName , passing the current state of the World , printing in output the
name you read from the standard input.
2. printString with a name, and then the function of type (World) ->
World with the current state of the World .
After you insert the name in input, you’ll get the output in Figure 16.2:
382
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
This is very interesting, but what you achieved now isn’t actually what you need.
You need a way to compose readName and printString as pure functions and get
an app that works like the initial one.
Pure greetings
To accomplish your goal, you basically need to create
askNameAndPrintGreetings , whose type is (World) -> World . The final state
of the world is the one where you asked for a name and printed a greeting
message.
Given readName and printString , you can add the following code to
Greetings.kt:
3. Invoke printString , passing the message to ask the name along with the
current state of the world, w0 . Here, you get the new state of the world you
store in w1 .
5. Invoke printString , passing the greeting message to print along with the
state of the world, w2 , you got in the previous step. printString also
returns the final state of the world you use as the return value for
askNameAndPrintGreetings .
fun main() {
askNameAndPrintGreetings()(World) pipe ::println
}
383
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
Here, you invoke askNameAndPrintGreetings , passing the initial state of the
world in input. When the function completes, you send the output to println .
Here, you:
Besides the fact that askNameAndPrintGreetings works, you should also note
these other crucial points:
At each step, you pass the current state of the world, getting the new state.
You’re not using World at all.
The last point is fundamental because it would be very useful to remove the
requirement of passing the world ahead. This problem is similar to the one
related to the State<S, T> monad you learned in Chapter 15, “Managing
State”.
384
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
typealias StateTransformer<S, T> = (S) -> Pair<T, S>
Note: You find the State<S, T> definition in the State.kt file in the lib sub-
package in this chapter’s material.
Here, you can follow the same process, defining the WorldT type like this in the
World.kt file:
Just be careful that T is a generic type parameter, but World is the actual type
you defined earlier as a simple type alias of Unit . If you now look at the
readName you defined in Greetings.kt, you can see that its type is exactly the
same as WorldT<String> . You can add the following definition without any
compilation errors in Greetings.kt:
What about printString ? Just remember that its type is (String) -> (World)
-> World , which is basically equivalent to (String)-> WorldT<Unit> . This is a
little bit more complicated, because it requires a little change. In Greetings.kt,
add the following definition:
Now, you need to solve a problem you’re used to: composition. You need to
compose printStringT with readNameT and printStringT again to
implement your beautiful Greetings program in a pure, functional way.
Before doing that, it’s useful to review the types of all the functions in play:
385
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
3. printStringT again.
One way to do that is the definition of a function — you temporarily define myOp
in World.kt with the following signature:
Note: You’ll find out the best name for myOp later. You might already know! :]
3. Returning a WorldT<B> .
This means that if you have a WorldT<A> and a function of type (B) ->
WorldT<B> , you can use the myOp operator and get a WorldT<B> .
To make all the types explicit, write its type like the following:
WorldT<A> // 1
-> (A) -> WorldT<B> // 2
-> WorldT<B> // 3
Remember that WorldT<A> is just an alias for the (World) -> Pair<A, World>
type, which allows you to rewrite the previous definition like the following:
386
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
This means you can compose the WorldT<A> in 1 with the uncurried version of
fn at 2. This allows you to write the implementation of myOp like the
following, which you should write in World.kt:
Note how you need to use uncurryP , a function you find in Curry.kt in the lib
sub-package of this chapter’s material. It uses Pair<A, B> as input in place of
two parameters of types A and B , respectively.
A hidden greeting
The first implementation of askNameAndPrintGreetings you created forced
you to carry the world on at each step.
Now it’s time to get rid of the world — wow! — and implement
askNameAndPrintGreetingsT , like the following you can write in Greetings.kt:
387
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
WorldT<Unit> , which is basically the world transformation encapsulating
Unit as a value.
fun main() {
askNameAndPrintGreetingsT()(World) pipe ::println
}
Here, you:
As you can see, you’ve got some good news and some bad news. The good news
is that the World is now hidden. In the askNameAndPrintGreetingsT body, you
don’t have to receive any World and pass it on to the following methods.
The bad news is that you probably don’t want to indent the code using all those
{} and create many lambdas as arguments of myOp .
Don’t worry. In the previous code, you definitely saw a lot of what you learned in
Chapter 15, “Managing State”. It’s now time to follow the same process and
388
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
define the IO<T> monad.
6. Finally, you’ll use IO<T> to solve the indentation problem you got with
askNameAndPrintGreetingsT , implementing a sort of monad
comprehension.
If you follow the same process you did for State<S, T> , replacing S with
World , the first five points are simple. You could do them as an exercise or just
follow along. :]
In the case of IO<T> , you just know that S in State<S, T> is the type World
and that instead of StateTransformer<S, T> , you have WorldT<T> .
Knowing how these types relate to each other, open IO.kt and add the following
definition:
Congratulations! You just created the IO<T> data type. As you’ll see, it’s very
389
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
simple and powerful. Now, it’s time to add even more power, starting with the
implementation of lift .
Implementing lift
As you know, lift is the function that allows you to get, in this case, an
IO<T> from a WorldT<T> . Depending on the context, you might find the same
function with a name like return or pure . Anyway, following the same
approach you saw in the previous section, you implement it by replacing the
existing code in IO.kt with the following:
companion object { // 1
@JvmStatic
fun <S, T> lift(
value: T // 2
): IO<T> = // 3
IO { w -> value to w } // 4
}
}
Here, you:
4. Create the IO<T> using the default constructor, passing a lambda of type
WorldT<T> that simply returns a Pair<T, World> in output.
To make the previous code simpler, the same way you did for State<S, T> , add
the following code:
This allows you to apply the WorldT<T> transformation in IO<T> using the
() operator directly.
IO<T> as a functor
The next step is to give IO<T> the power of a functor and provide an
implementation of map . This is usually very easy, and this case is no different.
Open IO.kt, and add the following code:
390
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
fun <A, B> IO<A>.map(
fn: Fun<A, B>
): IO<B> =
IO { w0 ->
val (a, w1) = this(w0) // Or wt(w0)
fn(a) to w1
}
Again, you just started from the same functions for State<S, T> , replaced
State with IO and removed S . In the implementation, you used wn instead
of sn to represent the n-th state of the world.
IO<T> as a monad
Finally, you want to give IO<T> the superpower of a monad, adding the
implementation of flatMap like this to IO.kt:
But, hey! You’ve seen this already, right? Earlier, you implemented myOp like
this:
391
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
Besides the fact that myOp is an extension function for WorldT<T> and
flatMap for IO<A> , they both accept a function that reminds you of the Kleisli
category.
The former accepts a function of type (A) -> WorldT<B> and the latter one of
type (A) -> IO<B> . Yeah, they represent the same concept!
But how can you use all this magic? With a monadic greeting, of course!
Monadic greetings
In the previous sections, you implemented askNameAndPrintGreetingsT like
this:
In this code, note how readNameT is just an alias for readName you used to
make its type, WorldT<String> , explicit. In any case, now you need to work
with IO<T> . To do this, write the following code in IOGreetings.kt:
392
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
Now, you have two functions working with IO<T> without exposing
WorldT<T> anymore. If you look at readNameM , you realize it returns an
IO<String> with the name you might’ve inserted during execution. Of course,
you need to access that value. To do this, just add the following code to the same
file:
This is simple and powerful because you can finally write the following in
IOGreetings.kt:
393
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
4. Print the output using printStringM . In this case, invoking bind() allows
you to return Unit from askNameAndPrintGreetingsIO . Without that,
you’d have returned IO<Unit> .
The only thing you need now is to test if it works. In the same IOGreetings.kt
file, add and run the following code:
fun main() {
askNameAndPrintGreetingsIO().invoke()
}
Here, you:
Just note how you don’t have to pass any World to the
askNameAndPrintGreetingsIO . The IO<T> monad does this all under the
hood.
Where:
394
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
What if readName fails for some reason? In that case, you should write a safe
version of it. Open Safe.kt, and write the following code:
1. safeReadName as a function that returns the String you read from the
standard input, encapsulated into a Result<String> .
2. safeReadNameError , which is a failing version of safeReadName .
This function is pure and works perfectly. To test this, just run the following
code:
fun main() {
safeAskNameAndPrintGreetingsIO().invoke().fold(
onSuccess = { _ ->
// All good
},
onFailure = { ex ->
println("Error: $ex")
}
)
}
396
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
To test how this works in case of error, just replace safeReadName with
safeReadNameError in the definition of safeReadNameT , like this:
This is very good, but it might look complicated. This is why some frameworks
like Arrow chose to use suspend functions instead of the IO<T> monad,
which is an excellent decision.
@DelicateCoroutinesApi
fun main() {
runBlocking { // 3
printStringCo("What's your name? ") // 4
val name = async { readStringCo() }.await() // 5
printStringCo("Hello $name!\n") // 6
}
}
1. Write the logic for reading the input String into readStringCo , which is a
suspendable function. It’s important to note how, in this case, the suspend is
redundant, but it allows you to mark it as a function that has a side effect.
2. Do the same for printStringCo that just prints the input String to the
standard output.
3. Just like IO<T> , a suspendable function allows you to wrap a side effect into
397
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
a block. This is very important because when doing this, you’re not actually
running that code — you’re just describing it. In this case, you’re saying that
there will eventually be some other component that runs that suspendable
function. In this case, that component is a Scheduler in the
CoroutineContext used by runBlocking .
Coroutines also allow you to handle exceptions in a robust and easy way.
Note: To learn more about coroutines, check out Kotlin Coroutines by Tutorials.
Key points
A pure function doesn’t have any side effects.
A side effect represents a change in the state of the world.
The State<S, T> data type allows you to handle state transitions in a
transparent and pure way.
You can think of the state of the world as a specific type S in State<S, T>
and consider StateTransformer<S, T> as a way to describe a
transformation of the world.
You can think of the IO<T> data type as a special case of State<S, T> ,
where S is the state of the world. In this way, all functions are pure.
398
Functional Programming in Kotlin by Tutorials Chapter 16: Handling Side Effects
399
Functional Programming in Kotlin by Tutorials
In Chapter 16, “Handling Side Effects”, you learned how to use the IO<T>
monad as a special case of the State<S, T> data type. You also learned that
Kotlin provides coroutines to handle side effects as pure functions in a more
idiomatic way. In this chapter, you’ll learn everything you need to know about
the following special Kotlin data types:
Sequence<T>
Flow<T>
You’ll learn how these data types work from a functional programming point of
view. In particular, you’ll answer the following questions for each of these:
Note: If you don’t know coroutines yet, Kotlin Coroutines by Tutorials is the book
for you.
This chapter is a big exercise that helps you take the concepts you’ve learned so
far and apply them to the types you use every day in your job. It’s time to have
fun!
400
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
fun main() {
listOf(1, 2, 3, 4, 5) // 1
.filter(filterOdd.logged("filterOdd")) // 2
.map(double.logged("double")) // 3
}
1. Use listOf as a builder for a List<Int> with five elements of type Int .
Note: filterOdd and double are two very simple functions you find in
Util.kt in the lib sub-package. logged is a utility higher-order function that
decorates another function with a log message. Take a look at their simple
implementation, if you want.
The interesting fact about the previous code happens when you run it, getting
the following output:
filterOdd(1) = false
filterOdd(2) = true
filterOdd(3) = false
filterOdd(4) = true
filterOdd(5) = false
double(2) = 4
double(4) = 8
3. Use map , getting a new List<Int> with the double of the values in the
previous List<Int> .
With this code, you basically created three lists without using any of the
individual lists’ values. What happens if you don’t really need the values in the
List<Int> ? In this case, you started with a List<Int> of five elements. What
if the list has a lot more elements? What if the elements in the List<T> are
infinite?
Well, you don’t have to blame the List<T> data type because its job is to
contain an ordered collection of elements of type T . That’s why it’s been
401
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
created that way. That’s its context, or purpose, if you will. Another way to say it is
that List<T> is eager.
If you don’t want to keep all the possible values in a List<T> , Kotlin provides
the Sequence<T> data type.
fun main() {
sequenceOf(1, 2, 3, 4, 5) // HERE
.filter(filterOdd.logged("filterOddSeq"))
.map(double.logged("doubleSeq"))
}
This code differs from the previous one because of the use of sequenceOf
instead of listOf . More importantly, if you run the code, you’ll get nothing as
output. This is because Sequence<T> is lazy. If you want to actually consume
the values in the Sequence<Int> you just created, you need to consume them
using a terminal operator. To see how, add .count() to the end of the method
chain. It should now look like this:
fun main() {
sequenceOf(1, 2, 3, 4, 5)
.filter(filterOdd.logged("filterOddSeq"))
.map(double.logged("doubleSeq"))
.count() // HERE
}
Here, you’re just counting the elements in the sequence and, to do it, you need
to consume all of them. This time, running the code, you’ll get the following:
filterOddSeq(1) = false
filterOddSeq(2) = true
doubleSeq(2) = 4
filterOddSeq(3) = false
filterOddSeq(4) = true
doubleSeq(4) = 8
filterOddSeq(5) = false
Note how the order of the log messages is different from the one you got from
the List<T> . In that case, each operator read the values from the input
List<T> . Now, the chain of operators is called for each value you consume.
402
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
This clarifies the context for a Sequence<T> as a container that produces the
values it contains only when required. That means it’s lazy. But is Sequence<T> a
functor?
Sequence<T> as a functor
Looking at the Sequence<T> documentation, you see the definition of map
with the following signature:
You used map in the example in the previous section. In this case, you want to
do something more and use property-based testing to prove the functor laws
for Sequence<T> .
Note: You already used property-based testing in Chapter 12, “Monoids &
Semigroups”.
You want to prove that, given the two functions f and g and the identity i :
map(i) == i
If you want to use property-based testing, you should define a way to generate
random functions using a specific implementation of the following interface
you find in PropertyTest.kt in the lib sub-package in this chapter’s material:
This describes the map function for a Generator<T> . It’s just a simple way to
create a Generator<R> from a Generator<T> using a function of type (T) ->
R or, using the typealiases in Definitions.kt, Fun<T, R> .
403
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
Note: Be careful that Generator<T> isn’t a functor because it’s not even pure:
It generates random values.
Every Fun<A, B> describes a way to map values of type A into values of type
B . The Fun<A, B> you’ll get from funGenerator is a function that maps the
same random value of type B to any values of type A you’ll pass as input to
the generated function. Because that return value is the same for all the input
values, you can assume that the same would happen for a specific value you
might generate during testing.
Open SequenceDataTypeTest.kt in the test build type, and add the following
code:
@Test
fun `Identity Functor Law for Sequences`() {
val intToStringFunGenerator =
funGenerator<Int, String>(StringGenerator(5)) // 1
val i = { s: String -> s } // 2
100.times { // 3
val f = intToStringFunGenerator.one() // 4
val seq = IntGenerator.generate(5).asSequence() // 5
val list1 = seq.map(f compose i).toList() // 6
val list2 = seq.map(f).toList() // 7
Truth.assertThat(list1).isEqualTo(list2) // 8
}
}
Here, you:
404
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
7. In the same way, you apply only f .
Run the test, and check that everything is successful. Now, you can be confident
that the first law of Sequence<T> as a functor is valid.
The second law is very simple. Just add this code to the same file:
@Test
fun `Composition Functor Law for Sequences`() {
val intToStringFunGenerator =
funGenerator<Int, String>(StringGenerator(5))
val stringToLongFunGenerator =
funGenerator<String, Long>(LongGenerator) // 1
100.times {
val f = intToStringFunGenerator.one()
val g = stringToLongFunGenerator.one() // 2
val seq = IntGenerator.generate(5).asSequence()
val list1 = seq.map(f compose g).toList() // 3
val list2 = seq.map(f).map(g).toList() // 4
Truth.assertThat(list1).isEqualTo(list2) // 5
}
}
Now, just run the test and see that all the tests are successful. Great job!
This is the signature for the ap function for a Sequence<T> . It’s an extension
function on the type Sequence<A> and accepts an input parameter of type
Sequence<(A) -> B> or Sequence<Fun<A, B>> if you use the type alias. The
return type is Sequence<B> . Basically, you have two sequences. The first
generates values of type A , and the second generates functions of type Fun<A,
B> . The result, then, is a Sequence<B> of the value you get by applying the
function in Sequence<Fun<A, B>> to the values in Sequence<A> . How would
you implement ap ?
Note: Feel free to provide your implementation as a fun exercise if you want.
2. Get the reference to the Iterator<A> from the Sequence<A> and iterate
over it.
4. Use yield to produce the value you get by applying the current function
Fun<A, B> to the current value A .
To see how it works, start by adding the following utility function. It allows you
to use ap as an infix operator, as you did with the other applicative functor
implementations:
406
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
fun main() {
data class User( // 1
val id: Int,
val name: String,
val email: String
)
val userSeq =
userBuilderSeq appl idSeq appl nameSeq appl emailSeq // 5
userSeq.forEach(::println) // 6
}
1. Create a User data class representing a user with id , name and email
properties.
2. Use curry to get the User constructor as a function of type (Int) ->
(String) -> (String) -> User .
3. Create a Sequence<(Int) -> (String) -> (String) -> User> using the
sequenceOf builder.
When you run this code, you’ll get 3 * 3 * 2 = 18 different values, like the
following:
407
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
Sequence<T> as a monad
Is Sequence<T> finally a monad? Of course it is, because of the flatMap
operation that Kotlin APIs provide with the following signature, similar to the
Kleisli category:
You can test how this works by running the following example:
fun main() {
// ...
val seqTo = { n: Int -> (1..n).toList().asSequence() }
val seqOfSeq = sequenceOf(1, 2, 3, 4, 5).flatMap(seqTo)
seqOfSeq.forEach { print("$it ") }
}
1 1 2 1 2 3 1 2 3 4 1 2 3 4 5
You can then say that the context of the Flow<T> data type is the generation of
a sequence of values you create using a suspendable function which, as you
know, allows you to handle side effects in a pure fashion.
Flow<T> as a functor
To prove that Flow<T> is a functor, you could repeat the same process you did
408
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
for Sequence<T> using property-based testing. In this case, you’ll keep things
easier, implementing some practical examples.
6. Emit the value from the user as a value from the Flow<String> .
fun main() {
val strLengthFlow = inputStringFlow("Insert a word: ") // 1
.map { str -> // 2
str to str.length
}
runBlocking { // 3
strLengthFlow.collect { strInfo -> // 4
println("${strInfo.first} has length ${strInfo.second}")
}
}
}
409
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
1. Use inputStringFlow to get a Flow<String> for the user input. Note that
you run this outside any specific CoroutineScope . You’re not executing
anything — you’re just stating you eventually might.
2. Invoke map , passing a lambda that returns a Pair<String, Int> of the
input String and its length. It’s important to note that the lambda here is
executed as a suspendable block. This means that it has a scope, and it can
contain invocations to other suspendable functions. In other words, using
map(String::length) would give a compilation error because
String::length isn’t a suspendable function. In your context, this also
means you can apply a transformation, Fun<A, B> , to the values you get
from a Flow<A, B> , which is the consequence of a side effect.
4. Collect and print the output values of type Pair<String, Int> you get from
the flow.
Now, you can run the code and get an output like the following:
To answer the initial question, yes, Flow<T> is a functor, but remember that
the transformation Fun<A, B> must be a suspendable function.
410
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
fun <A, B> Flow<A>.ap(fn: Flow<(A) -> B>): Flow<B> = flow { // 1
collect { a -> // 2
fn.collect { f -> // 3
emit(f(a)) // 4
}
}
}
Here, you:
2. Collect the value of type A from the Flow<A> you have as the receiver.
3. Collect the function f of type Fun<A, B> from the Flow<Fun<A, B>> or
Flow<(A) -> B> you pass as the input parameter fn .
You can then test everything by adding the following code to the same file:
fun main() {
val userBuilder = { id: Int ->
{ name: String ->
{ email: String -> User(id, name, email) }
}
}
val userFlow =
userBuilderFlow appl idFlow appl nameFlow appl emailFlow
runBlocking {
userFlow.collect(::println)
}
}
This code shouldn’t be a surprise anymore. When you run it, you’ll get:
411
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
The same notes you learned for Sequence<T> are valid here.
Flow<T> as a monad
To answer the last question, you’ll implement a more complex example using
some of the code you already implemented in Chapter 14, “Error Handling With
Functional Programming”, that you can find in the material for this project. You
basically want to use inputStringFlow to allow a user to insert some text to
search for in the TV show database using the TVmaze API. Now, you’re in the
world of coroutines, so you should use their power. It’s time for an interesting
exercise to improve your functional thinking.
Imagine you have a basic function, like the following you can write in Basic.kt:
It doesn’t really matter what doSomeWork does. What’s important is the type,
which is (String) -> Int , and how the function achieves its goal. If it needs to
do some hard work, you probably want to run it in the background, so in the
context of a coroutine. You can do this with the following code, which you
should add to the same file:
Is a suspend function.
Accepts two parameters now. The first is of type CoroutineContext , and the
second is the input for doSomeWork .
You already know that in functional programming, you don’t like functions with
multiple parameters. No problem — you also know you can curry them, but
with doSomeBgWork , there’s a problem. You can see the problem by adding the
following code:
412
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
fun main() {
doSomeBgWork.curry()
}
The reason is very simple. When you set a function as suspend , you’re
basically changing its type. The Kotlin compiler adds implicit parameters of type
Continuation that keep track of the state of the coroutine.
Note: Look at the decompiled code, and you can see how the Continuation is
used in a way that reminds you how you implemented the State<S, T> and
IO<T> data types.
How can you then implement curry for a suspendable function? You already
know that. In the same Basic.kt file, add the following definitions:
In this simple code, you define an alias for suspendable functions of:
2. Two input parameters of type A and B and one output parameter of type
C .
413
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
CoroutineContext as a state
To relate the State<S, T> monad to what you saw about suspendable
functions, look again at doSomeBgWork , which you wrote in Basic.kt:
414
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
(String) -> (CoroutineContext) -> Pair<CoroutineContex, Int> .
Note: Yes, the CoroutineContext and String types have different orders,
but you already know how to implement flip , right?
Now, the analogy with State<S, T> is almost obvious. You just need to fix a
problem caused by the presence of the suspend modifier. No problem.
Ring a bell now? Using this definition, the type of doSomeMoreBgWork is suspend
(String) -> SuspendStateTransformer<CoroutineContext, Int> .
You can now follow the same process you did for State<S, T> , keeping in
mind you’re working with suspendable functions, and the state is a
CoroutineContext .
companion object {
@JvmStatic
fun <S, T> lift(
value: T
): SuspendableState<S, T> =
SuspendableState { state -> state to value }
}
}
415
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
Finally, add the implementation for flatMap like this:
Note: This time, you can’t override the invoke operator as you did in a non-
coroutine environment. To be useful, it should also be suspendable, and this
wouldn’t work.
How can you use this for your initial TV show problem? There’s actually quite a
bit more fun in store for you. :]
Look at the existing code, and you’ll see that TvShowFetcher and
TvShowParser don’t actually handle exceptions. This is also why you used
those objects in many different ways in Chapter 14, “Error Handling With
Functional Programming”.
Now, you want to run them as side effects in a suspendable function and handle
errors. How can you do that?
416
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
This code should look familiar, even if it combines a few concepts. Here, you:
2. Set Result<String> as the return type. This is a little bit more complicated
than a simple String . You’ll need to do some more work because of this, as
you’ll see later.
For TvShowParser.parse , you can follow the same pattern, adding this to the
same file:
You can try to solve this problem by adding the following code:
417
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
Now:
Composing SuspendableState<CoroutineContext,
Result<T>>
To implement composition now is simpler than it seems. Open
SuspendableStateResult.kt, and add the following code:
companion object {
@JvmStatic
fun <S, T> lift( // 3
value: T
): SuspendableStateResult<S, T> =
SuspendableStateResult { state ->
state to Result.success(value)
}
}
}
418
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
fn: SuspendFun<A, B>
): SuspendableStateResult<S, B> =
SuspendableStateResult { s0: S ->
val (s1, a) = this.sst(s0)
s1 to a.fold(
onSuccess = { Result.success(fn(it)) },
onFailure = { Result.failure(it) }
)
}
It’s a lot of code, but everything should be clear. In particular, you define:
Finally atMap
It’s finally time to put everything together so you can access the TVmaze
database. Open ShowSearchService.kt, and add the following code:
419
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
You can now compose these functions. Just add the following code to the same
file:
@OptIn(FlowPreview::class) // 1
suspend fun searchTvShow(ctx: CoroutineContext) = // 2
withContext(ctx) {
inputStringFlow("Search Your Show: ") // 3
.flatMapConcat { query -> // 4
fetchSuspendResult(query)
.flatMap(parseSuspendResult).sst(ctx) // 5
.second.fold(
onSuccess = { it.asFlow() }, // 6
onFailure = { emptyFlow() }) // 7
}
}
5. Pass the CoroutineContext you get as input to the function you get from
the composition of fetchSuspendResult and parseSuspendResult .
6. Return the value in Result<List<ScoredShow>> as a Flow<ScoredShow> in
case of success.
420
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
@OptIn(FlowPreview::class)
fun main() {
runBlocking { // 1
searchTvShow(Dispatchers.IO) // 2
.collect { // 3
println("Score: ${it.score} " +
"Name: ${it.show.name} " +
"Genres: ${it.show.genres}") // 4
println(it.show.summary)
println("--------------------------")
}
}
}
Here, you:
1. Use runBlocking to give some scope to searchTvShow .
2. Invoke searchTvShow , passing Dispatchers.IO as CoroutineContext .
This allows your code to run in the background.
3. Collect all the results.
Now, you can run the previous code and have some fun, like this:
Great job!
421
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
For this reason, all the concepts you’ve seen so far are also valid for
SharedState<T> and StateFlow<T> .
Key points
The List<T> data type allows you to store an ordered collection of elements
of type T in an eager way.
422
Functional Programming in Kotlin by Tutorials Chapter 17: Sequence & Flow
As mentioned previously, you can take a look at Kotlin Coroutines by Tutorials to
learn more about coroutines, SharedFlow<T> and StateFlow<T> . The
following chapters will talk about a couple more libraries that embody
functional programming principles.
423
Functional Programming in Kotlin by Tutorials
18 Mobius — A Functional
Reactive Framework
Written by Massimo Carli
In Chapter 15, “Managing State”, you learned the importance of the concept of
state. A state is usually defined as some value that can change over time. You
also learned that what you consider a state defines side effects. A side effect is
something that changes the state of the world, which is outside the context of a
function.
Side effects aren’t harmful as long as you can control them. In Chapter 16,
“Handling Side Effects”, you saw how important it is to separate the description
of an effect from its actual execution.
These are all the fundamental principles used by Mobius, which is defined as: “a
functional reactive framework for managing state evolution and side-effects,
with add-ons for connecting to Android UIs and RxJava Observables. It
emphasizes separation of concerns, testability, and isolating stateful parts of the
code”.
You’ll do this by creating a Mobius version of the RayTV app you met in Chapter
14, “Error Handling With Functional Programming”. You’ll call it Raybius. :]
Note: The Raybius app uses Dagger and Hilt. If you want to learn all about the
Android dependency injection framework, Dagger by Tutorials is the perfect
book for you.
424
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
This image has numerous interesting concepts you can easily understand by
following the flow described earlier.
When you launch your app, you can see a UI, which is usually a composition of
views, like TextView s, Button s and so on. You can think of a view as a way to
represent some data. When the data changes, the UI usually changes. This is
important because you can think of the data as the current state of the UI. The
data you want to display with the UI is usually represented as the model.
As mentioned earlier, the user interacts with the UI by pressing some buttons or
providing some data as input. Mobius represents these actions as events. An
event is what makes an app interesting. Some events just update the UI, creating
a new model to display. Others are more complicated because they trigger a
request to the server or access to a database.
To handle both use cases, Mobius provides an Update function. It receives the
current model and the input event, and returns the new model and an optional
description of a side effect. It’s crucial to see how the Update function lives in the
425
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
pure section of the diagram. The Update function is pure. This is because:
The new model just depends on the input model/state and event.
This makes the Update function very easy to test. Not represented in Figure
18.1 is the Init function. Init is a version of Update that generates the first
state and, optionally, the first set of effects to generate.
Now, Mobius sends the new model to the UI and the optional effects to some
effect handlers. These are what actually execute the side effects, usually in a
background thread. It’s also interesting to see that effect handlers notify the
outcome using events and how they go through the same flow you saw earlier for
the events from the UI.
The Update function is invoked, and a new state/model is created along with
other optional side effect descriptions.
Models
Events
Effects
Init functions
Update functions
This implies that creating an app with Mobius means defining these same
concepts in the domain of the app itself. This leads to a sequence of steps you
can follow every time in the design and implementation of your app. This
426
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
process has a name: the Mobius workflow. It consists of the following four steps:
2. Describe
3. Plan
4. Build
It’s interesting now to see each of these in detail in the context of the Raybius
app.
An external event.
External events definition: In your case, the Raybius app doesn’t handle any
external events, but it could. For instance, you could display a message and
disable the input if the device is offline and then restore that functionality when
it reestablishes a connection. To track all the use cases, you create a table like
the following. This is the MoFlow table.
Because you don’t have external events, the table is initially empty. No problem!
You deal with user interactions next.
Capture user interactions: These represent the possible events you can
generate when the user interacts with the app. In your case, you can enter the
name of a TV show in the EditField and tap the button to perform the search.
This leads to the following events:
427
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
When the user changes the text to use as input, the app generates an
InputTextChanged with the new text. When the user clicks SEARCH, it triggers
SearchButtonClicked .
Define effects: Here, you need to think about the effects the previous events
can generate. In your case, you only have one effect — the one related to the
request to the server for fetching the information about your TV show. Call this
effect SearchTvShow , and add it to the table like this:
Define your model: In this step, you need to define the model as a
representation of your app’s view. In Raybius, you enter text, so you probably
want to disable the SEARCH button if the text field is empty or the text string is
too short. So, the model should then contain some kind of property that enables
or disables the SEARCH button. If you decide to handle the offline case, you also
need a variable that disables all the features or displays some messages. For this,
you can define TvShowModel , which makes your MoFlow table like the
following:
428
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
The view of your app will use all this data to render the information you need.
Define effects feedback events: Now, you have TvShowModel to keep track of
the current state of the input text, and you know what events to generate when
the user interacts with the app. You also know that you want the app to execute
an effect for accessing the server to fetch the information about the input show.
Hopefully, this effect will produce some results or — hopefully not — some
errors. As you learned in Figure 18.1, effects notify some results using other
events. In the case of Raybius, you can have a SearchSuccess containing the
actual results and SearchFailure in case of errors. This leads to the following
new version of the MoFlow table:
But what do you do in case of error? You probably need to display some error
messages using, for instance, a Toast . To do this, you might need other events
or, like in this case, a new effect. In case of success, you’ll probably want to
select an item from a list and display the details for the show. This leads to the
ItemClicked event and the GetTvShowDetail effect, which then leads to the
TvShowDetailSuccess and TvShowDetailFailure events. Moving from the list
to the detail allows you to define the NavigateToDetail effect. When you get
results, you probably want to hide the keyboard — you can do this with the
HideKeyboard effect. The DetailViewResumed event is useful to trigger the
GetTvShowDetail effect when the detail screen is displayed.
429
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
Now, you have a clear understanding of what your app should do and how it
should behave when different events are triggered. You’d usually do MoFlow on
a whiteboard and with the help of your designer or other stakeholders.
To see how this is implemented in Raybius, open the starter project in this
chapter’s material in Android Studio, and run the app. You’ll see the following
screen:
430
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
Note how the SEARCH button is disabled. This is because the text field input is
empty. When you insert a text string longer than three characters, you’ll see the
button enabled.
When you click it, you’ll see the results in a list like this:
431
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
Also, note how the keyboard is now hidden. At the moment, you can’t select a
show, but you’ll implement that feature later in the chapter. Now, look at the
existing code. Open TvShowModel.kt in the raybius.mobius package, and look
at the current implementation for the model:
This is a simple data class containing the properties that tells the view:
3. Whether the app is loading some data. This is useful for displaying a spinner.
4. What the existing results to display are.
This is quite simple and describes how the view changes when a new model
needs to be rendered.
To see what happens when the user taps SEARCH, look at the initUI function
in the same file.
432
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
fun FragmentSearchBinding.initUI(
eventConsumer: Consumer<TvShowEvent>,
onItemSelected: (Int) -> Unit
) {
with(resultRecyclerView) {
adapter = ScoredShowAdapter(onItemSelected)
layoutManager = LinearLayoutManager(context)
visibility = View.GONE
}
helpMessage.visibility = View.VISIBLE
progressBar.visibility = View.GONE
textUpdate { text ->
eventConsumer.accept(InputTextChanged(text)) // 1
}
search {
eventConsumer.accept(SearchButtonClicked) // 2
}
}
Later, you’ll see where the Consumer<TvShowEvent> comes from, but for now,
you can think of it as the tool to interact with the Mobius loop you saw in Figure
18.1.
To see the events, just open TvShowEvent.kt in mobius, and see they’re very
simple data classes or objects:
Now, you’ve seen how to send events and how to change the UI based on the
current model. But the core of the Mobius architecture is the Update function.
Open TvShowLogic.kt in the mobius package, and look at the following code:
433
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
model.copy(
searchEnabled = event.text.length >= 3,
inputText = event.text
)
)
is SearchButtonClicked -> Next.next( // 2
model.copy(loading = true),
setOf(SearchTvShow(model.inputText))
)
is TvSearchSuccess -> Next.next( // 3
model.copy(
searchResults = event.results,
searchEnabled = true
), setOf(HideKeyboard)
)
is TvSearchFailure -> Next.next( // 4
model.copy(
error = true,
searchEnabled = true
), setOf(
HideKeyboard, DisplayErrorMessage(
event.ex
)
)
)
else -> Next.noChange()
}
}
This is a simple function that translates what you’ve designed in the MoFlow
table into code. For instance, you can see that:
1. If you receive an InputTextChanged event, you check if the text in input has
at least 3 characters and generate a new TvShowModel with the
searchEnabled property set to true and the inputText with the new text.
In this case, you don’t trigger any effects.
4. In case of error, you receive a TvSearchFailure , and you then trigger the
DisplayErrorMessage effect to display an error message along with the
HideKeyboard effect.
This function is easy to read and pretty straightforward to write. All good, but
what about the effects?
434
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
described in the Update function. The actual code should be part of the next
step, but right now, you’ll look at how different effects are executed. You already
know that an effect is basically the description of an operation that changes the
external world. The component responsible for actually executing an effect is
called an effect handler. Mobius provides different ways to implement an effect
handler.
In the case of the Raybius app, you have two of them. Open TvShowEffect.kt
and look at the following effects’ definitions:
For each of them, you need to define an effect handler. Some are very simple
and just need to consume the information into the effect class. Others are more
complicated and need to generate some events as the result of the effect.
DisplayErrorMessage is in the first category. Open UIEffectHandlerImpl.kt in
mobius.handlers, and look at the following code:
Here, you just consume the DisplayErrorMessage and use the Context you
inject to display a Toast .
435
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
onFailure = {
Observable.just(TvSearchFailure(it))
} // 3
)
}
OK, but how do you tell Mobius what effect handler to use for every effect? In
the Raybius app, this is done in MobiusModule.kt in di with the following code:
@Provides
fun provideEffectHandler(
uiHandler: UIEffectHandler,
apiRequestHandler: ApiRequestHandler
): TvShowEffectHandler =
RxMobius.subtypeEffectHandler<TvShowEffect, TvShowEvent>()
.addTransformer(
SearchTvShow::class.java,
apiRequestHandler::handleSearchTvShow
) // 1
.addConsumer(
DisplayErrorMessage::class.java,
uiHandler::handleErrorMessage, // 2
AndroidSchedulers.mainThread()
)
.addConsumer(
HideKeyboard::class.java,
uiHandler::handleHideKeyboardMessage,
AndroidSchedulers.mainThread()
)
.build();
2. DisplayErrorMessage to uiHandler::handleErrorMessage .
Besides some implementation details you can see directly in the project or the
Mobius official documentation, this is basically all you need to design and
implement your app with Mobius. So, now it’s time to implement some code
yourself.
436
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
1. Update the TvShowModel with the data you need to display the ShowDetail .
4. Update the Update function (pun intended :]) to handle the new use case.
Model update
Open TvShowModel.kt, and add the following constructor property:
This will contain a ShowDetail , which has the detailed information for a TV
show.
437
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
2. Handle the selection of an item in the list result for the first query.
3. Handle a successful response for the detail.
Adding effects
Add new effects. Open TvShowEffect.kt, and add the following code:
Here, you:
1. Trigger the NavigateToDetail effect when selecting an item in the list and
438
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
then receive an ItemClicked event.
Now, you’ve defined some new effects and bound them to some events. At the
moment, Mobius doesn’t actually know how to execute them. It’s time to
implement the effect handlers.
fun handleNavigateToDetail(
request: Observable<NavigateToDetail>
): Observable<TvShowEvent>
DetailViewResumed(request.showId)
}
439
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
network to fetch the TV show details, which needs an effect handler. Open
ApiRequestHandler.kt, and add the following definition:
fun handleTvShowDetail(
request: Observable<GetTvShowDetail>
): Observable<TvShowEvent>
As before, you’re just defining the operation for an effect handler that
consumes a GetTvShowDetail and generates a TvShowEvent . For the
implementation, open ApiRequestHandlerImpl.kt, and add the following code,
which should be quite familiar:
Now, you’ve implemented the code for the new effect handlers, but Mobius
doesn’t know about them yet. Next, open MobiusModule.kt, and add the
following transformers to provideEffectHandler below the existing
transformer:
.addTransformer(
GetTvShowDetail::class.java, // 1
apiRequestHandler::handleTvShowDetail
)
.addTransformer(
NavigateToDetail::class.java, // 2
uiHandler::handleNavigateToDetail
)
In this code, you use addTransformer to tell Mobius what effect handler to
execute for the effects:
1. GetTvShowDetail
2. NavigateToDetail
Now, you just need to update the UI logic for this new feature.
Update UI logic
You still need to do a few last things to handle user events and use the data you
440
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
receive from the new effects. You basically need to:
if (model.detailResult != null) { // 1
displayResult(model.detailResult) // 2
}
Now, you can build and run the app. When you select an item in the list of
results, you can see how the detail screen is displayed, like in Figure 18.10:
441
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
Great job! As a last note, it’s worthwhile to have a quick look at how you set up
the Mobius loop in Android.
Usually, there’s an instance of the Mobius loop per surface when a surface is
basically a screen of the app. You can decide to use a single Mobius loop or
create multiple ones depending on the dimension of the app. In the case of
Raybius, a single Mobius loop is shared between all the Fragment s through the
MainActivity class. To bind the lifecycle of the Mobius loop to the Activity
one, Mobius provides a MobiusLoop.Controller . If you look at MainActivity ,
removing the unrelated things, you’ll get the following:
442
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials
@AndroidEntryPoint A Functional Reactive Framework
class MainActivity :
AppCompatActivity(), MobiusHost<TvShowModel, TvShowEvent> {
@Inject
lateinit var tvShowController: TvShowMobiusController // 1
var logic: (
Consumer<TvShowEvent>, TvShowModel
) -> Unit = { _, _ -> }
override fun injectLogic(
logic: (Consumer<TvShowEvent>, TvShowModel) -> Unit
) { // 8
this.logic = logic
}
}
In this code:
443
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
returns an object of type Connection<TvShowModel> . The object
Connection<TvShowModel> needs to override accept and dispose .
5. accept is invoked every time the Mobius loop needs to deliver a new model.
This is where you bind the logic specific to your app.
6. dispose is invoked when you stop the loop.
8. This is a function each Fragment can invoke to set its specific logic. This
doesn’t allow more Fragment s to be visible at the same time, but for this
app, this is an acceptable trade-off.
Key points
Mobius is a functional reactive framework for managing state evolution and
side effects, with add-ons for connecting to Android UIs and RxJava
Observables.
The MoFlow is a process that allows you to design your app in terms of
models, events and effects.
The Update function is a pure function, receiving the current model and
the event as input, and returning the new model and the optional effects.
444
Chapter 18: Mobius —
Functional Programming in Kotlin by Tutorials A Functional Reactive Framework
445
Functional Programming in Kotlin by Tutorials
19 Arrow
Written by Massimo Carli
Note: If you want to learn all about Arrow, read Arrow’s official
documentation, which is perfectly maintained by 47 Degrees.
To cover all the features this library provides in a single chapter is impossible.
For this reason, you’ll focus on a specific problem: error handling. Using Arrow,
you’ll see how to handle errors in a functional way and learn what data types
Arrow provides. This is the Arrow solution to what you did in Chapter 14, “Error
Handling With Functional Programming”.
What Arrow optics are and how you can use them to handle complex
immutable objects easily.
446
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
the time, doesn’t contain all the information you need. In Java — and Kotlin —
you also have different types of exceptions that differ in name and not much
more.
To see what tools Arrow provides for handling exceptions in a functional way,
you’ll start with the same code you wrote in Chapter 14, “Error Handling With
Functional Programming”, to fetch and parse data about some TV shows. Open
the starter project in this chapter’s material, and look at the code in the tools
subpackages. In TvShowFetcher.kt in tools.fetchers, you’ll find the following
code:
object TvShowFetcher {
fun fetch(query: String): String {
val encodedUrl = java.net.URLEncoder.encode(query, "utf-8")
val localUrl =
URL("https://api.tvmaze.com/search/shows?q=$encodedUrl")
with(localUrl.openConnection() as HttpURLConnection) {
requestMethod = "GET"
val reader = inputStream.bufferedReader()
return reader.lines().toArray().asSequence()
.fold(StringBuilder()) { builder, line ->
builder.append(line)
}.toString()
}
}
}
This is quite straightforward and allows you to query the TVmaze database by
passing a string as input and getting a JSON with the response as output. Run
the previous method, executing the following main :
fun main() {
fetch("Big Bang") pipe ::println
}
[{"score":1.1548387,"show":
{"id":58514,"url":"https://www.tvmaze.com/shows/58514/big-
bang","name":"Big bang","type":"Panel
Show","language":"Norwegian","genres":
["Comedy"],"status":"Ended","runtime":60,"averageRuntime":60,
// ...
canine sidekick, Lil' Louis.</p>","updated":1628627563,"_links":
{"self":
{"href":"https://api.tvmaze.com/shows/10115"},"previousepisode":
{"href":"https://api.tvmaze.com/episodes/531559"}}}}]
You can also simulate a case where something goes wrong. Disconnect your
machine from the network and run the main again — you’ll get the following
447
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
exception:
What’s important here is that you have a TvShowFetcher that provides fetch
to query TVmaze about a TV show, and this can either succeed or fail.
object TvShowParser {
This uses the Kotlin serialization library to parse the input JSON into a
List<ScoredShow> . It can also either succeed or fail. To test the second case,
simply run:
fun main() {
TvShowParser.parse("Invalid JSON") pipe ::println
}
Given these two functions, how can you fetch and parse the JSON from the
server in a functional way? You already know the answer, but it’s useful to see
what Arrow provides.
448
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
Using Option<T>
A first possible solution to the previous problem is the Option<T> data type
Arrow provides with the core library. Find it already added it to your project
with the following definition in build.gradle:
Note: Notice that the name for this data type is Option<T> , not Optional<T> .
To see how this works, open TvShowOption.kt, and add the following code:
fun parseOption(
json: String
): Option<List<ScoredShow>> = try { // 1
TvShowParser.parse(json).some() // 2
} catch (e: Throwable) {
none() // 3
}
If you look at some and none implementation, you find simple code like this:
449
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
Where Some<T> and None are specific definitions of the sealed class
Option<T> , just as in the previous chapters. This is all quite simple.
Now, you need to fetch and parse the result while handling possible failure. You
already know how to do it. Just add the following code to the same file:
fun fetchAndParseOption(
query: StringBuilder
): Option<List<ScoredShow>> =
fetchOption(query)
.flatMap(::parseOption)
The Option<T> data type provides flatMap to compose functions the same
way you did in the previous chapters.
fun main() {
val searchResultOption = fetchAndParseOption("Big Bang") // 1
if (searchResultOption.isDefined()) { // 2
val searchResult =
searchResultOption.getOrElse { emptyList() } // 3
if (!searchResult.isEmpty()) {
searchResult.forEach { // 4
with(it.show) {
println(
"Name: ${name} Genre: ${genres.joinToString()}"
)
}
}
} else {
println("No Results!") // 5
}
} else {
println("Something went wrong!") // 6
}
}
2. Use isDefined to check if the Option<T> has a value and if it’s a Some<T>
or None .
4. Check that the result isn’t empty, and display all the ScoredShow data as
450
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
output, iterating over the resulting List<ScoredShow> .
This is a pretty good solution if you’re not interested in the specific reason for
failure.
At this point, you might ask why you need Option<T> in Kotlin when you
already have the opportunity to use optional types. This is an excellent question,
and Arrow has an answer for it.
Using the flatMap implementation you find in Nullable.kt in the lib package,
add the following:
451
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
Arrow provides you an alternative solution. In the same file, add the following
code:
You invoke fetchNullable and parseNullable one after the other in the
same way you would in a procedural case. The magic is provided by the
nullable function but also by the bind you invoke on the result of each
invocation. This is how Arrow handles NullableEffect<T> , which is an
Effect<T> that can result in a null value.
452
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
Which isn’t very different from the one you would’ve executed in the case of
using flatMap , which is:
fun mainWithFlatMap() { // 1
val searchResultOption = fetchAndParseNullableFlatMap("Big Bang")
// 2
if (searchResultOption != null) {
printScoresShow(searchResultOption) // 3
} else {
println("Something went wrong!")
}
}
Finally, run the following main . This lets you check how the two
implementations work basically the same — besides the need for a scope for the
suspendable one:
fun main() {
mainWithFlatMap()
runBlocking {
mainWithComprehension()
}
}
Which one is the best? The answer is always the same: It depends! :] If you need
to compose different functions that execute tasks in the background, the Arrow
solution is probably the best.
In both cases, you get a nullable object that doesn’t give you any information
about what happens in the case of failure. You just get null without any other
information. You’ve already met this problem in the previous chapters, and you
already have a first solution: the Either<A, B> data type.
453
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
fun fetchEither(
query: String
): Either<IOException, String> = try { // 1
TvShowFetcher.fetch(query).right() // 2
} catch (ioe: IOException) {
ioe.left() // 3
}
fun parseEither(
json: String
): Either<Throwable, List<ScoredShow>> = try { // 1
TvShowParser.parse(json /* + "break" */).right() // 2
} catch (e: Throwable) {
e.left() // 3
}
fun fetchAndParseEither(
query: String
): Either<Throwable, List<ScoredShow>> =
fetchEither(query)
.flatMap(::parseEither)
fun main() {
fetchAndParseEither("Big Bang") // 1
.fold( // 2
ifRight = ::printScoresShow, // 3
ifLeft = ::printException // 4
)
}
454
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
Here, you:
2. Use fold to handle success cases, the right, as well as failure cases, the left.
Error api.tvmaze.com
To simulate the failure of parseEither , just remove the comments from the
code like here and, of course, restore your connection:
This works exactly like the Either<A, B> data type you implemented in
Chapter 9, “Data Types”, but Arrow gives you the monad comprehension power
as well. In this case, you can use the either function like this:
455
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
Here, you:
4. Use the result you get from fetchEither as input for parseEither and
invoke bind again.
To test this code, just repeat the previous scenarios on the following:
fun main() {
runBlocking {
fetchAndParseEitherComprehension("Big Bang")
.fold(
ifRight = ::printScoresShow,
ifLeft = ::printException
)
}
}
You can easily verify that the results will be the same.
Arrow optics
One of the most important principles in functional programming is
immutability. An object is immutable if it doesn’t change its state after
creation. A class is immutable if it doesn’t provide the operation to change the
state of its instances.
As you know, the state of an object is the set of values for all its properties.
Immutable objects have many different advantages. For instance, different
threads can safely share them without any risk of deadlock or race conditions.
Sometimes you need to “update” the state of an immutable object. Wait, what?
Well, in Chapter 18, “Mobius — A Functional Reactive Framework”, you saw that
the Update function returns the new state and an optional set of effects, given
the current state and the information about an event. Often, the new state has
different values for some properties, leaving the remaining unchanged.
To solve problems like this, Arrow provides the optics library. It’s an automatic
DSL that allows users to use dot notation when accessing, composing and
transforming deeply nested immutable data structures.
To understand how this works, open Optics.kt, and add the following code:
456
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
val bigBangTheory =
ScoredShow(
score = 0.9096895,
Show(
id = 66,
name = "The Big Bang Theory",
genres = listOf("Comedy"),
url = "https://www.tvmaze.com/shows/66/the-big-bang-theory",
image = ShowImage(
original = "", // HERE
medium =
"https://static.tvmaze.com/uploads/images/medium_portrait/173/43386
8.jpg"
),
summary = "<p><b>The Big Bang Theory</b> is a comedy about
brilliant physicists, Leonard and Sheldon...</p>",
language = "English"
)
)
Here, you just create bigBangTheory with the values you get from the TVmaze
database. To make the core more readable, you used named parameters and
made the summary shorter. Now, imagine that when you got this data, the
original version for the image wasn’t available, as you can see with the
empty value. Now that information is available, and you want to update
bigBangTheory with the new value.
You can check that this works by running the following code:
fun main() {
bigBangTheory pipe ::println
updatedBigBangTheory pipe ::println
}
457
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
And check that the original property now has a value.
As mentioned, this works, but that’s a lot of code for just updating a property of
an immutable object. With Arrow, you can use lenses, which are provided with
the optics library. A lens is exactly what its name suggests. It’s a way to reach a
property that might be very deep in a hierarchy of objects and basically see it as if
it were closer.
Note: The project is already configured to use optics. Please be careful if you
want to update the library and plugin versions because they’re strictly
connected. A wrong version could cause the code to simply not work.
To use a lens, open Show.kt in model, and update all the data classes in it like
this:
@optics // 1
@Serializable
data class ScoredShow(
val score: Double,
val show: Show
) {
companion object // 2
}
@optics // 1
@Serializable
data class Show(
val id: Int,
val name: String,
val genres: List<String>,
val url: String,
val image: ShowImage?,
val summary: String?,
val language: String
) {
458
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
companion object // 2
}
@optics // 1
@Serializable
data class ShowImage(
val original: String,
val medium: String
) {
companion object // 2
}
1. Annotate each data class with @optics . This will cause the Arrow compiler
to generate all the code you need.
2. Provide a companion object to each of the @optics classes. Arrow needs
this to attach all the generated extensions functions.
fun main() {
val updateOriginalImageLens: Optional<ScoredShow, String> =
ScoredShow.show.image.original // 1
val updatedShow =
updateOriginalImageLens.modify(bigBangTheory) { // 2
"https://static.tvmaze.com/uploads/images/medium_portrait/173/43386
8.jpg"
}
updatedShow pipe ::println // 3
}
Note: Make sure you rebuild the project and import the generated show and
image properties to get this to compile.
Here, you:
459
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
worked.
Arrow lenses
In the previous section, you learned that optics are basically abstractions that
help you update immutable data structures in an elegant and functional way. A
lens is an optic that can focus into a structure and get, modify or set the value
of a particular property.
In terms of functional types, you can say that a Lens<T, A> is a way to
aggregate a couple of functions:
get of type (T) -> A , which extracts the value of type A from the object
of type T .
setter of type (A) -> (T) -> T which, given a value of type A for a
property, provides the function you run to update the object of type T .
Given a Lens<T, A> , you call T the source of the lens and A its target.
Lenses can be seen as a pair of functions: a getter and a setter. A Lens<S, A>
represents a getter: get: (S) -> A, and setter: set: (A) -> (S) -> S ,
where S is the source of the lens and A is the focus or target of the lens.
460
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
In this code, you define:
2. The get function using a lambda that simply accesses and returns the value
of the genres property of the show for the ScoredShow in input.
3. The set function using a lambda receiving as input a ScoredShow and the
List<String> for the genre to append. Note how you use the lenses Arrow
already creates for you, as you learned above.
To use addGenreLens , simply add the following code to the same file:
fun main() {
addGenreLens.set(
bigBangTheory, listOf("Science", "Comic")
) pipe ::println
}
Run it, and you’ll get the following output with the new genres added:
But lenses aren’t just a way to write more concise code. Suppose you want to
update the name for a given show. In the same file, add the following code:
If you want to then update the name of a Show of a given ScoredShow , you can
461
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
simply compose the previous lenses like this:
fun main() {
// ...
val updateName = showLens compose nameLens
updateName.modify(bigBangTheory, String::toUpperCase) pipe
::println
}
Key points
Arrow is a library maintained by 47 Degrees that allows you to apply
functional programming concepts to your Kotlin code.
Arrow provides the implementation for the most important data types, like
Optional<T> , Either<A, B> , Monoid<T> and many others.
Using the optics library, you can generate the code for reducing boilerplate
in the case of handling immutable objects.
A lens is an abstraction that helps you access properties and create new
objects from existing immutable ones.
462
Functional Programming in Kotlin by Tutorials Chapter 19: Arrow
463
Functional Programming in Kotlin by Tutorials
20 Conclusion
Written by Massimo Carli
Congratulations! You’ve sure come a long way from the beginning of this book.
You started with learning some of the very basic fundamentals of functional
programming, all the way to discovering how to build on those fundamentals to
manage state changes and error handling safely in your real-world applications.
That’s no small feat!
You stuck it out through some intense chapters. Maybe you read them and
reread them. Or maybe you still feel you need to read them again — that’s
completely expected, by the way. These chapters each build on each other, so
concepts you learned later in the book will help you when you return to earlier
chapters. It’s almost like a Choose Your Own Adventure book.
Toward the end of this book, you got a taste of how Kotlin coroutines can work
with your functional code. To continue learning about coroutines, we
recommend reading Kotlin Coroutines by Tutorials. While not strictly related, if
you enjoyed this topic, you might also enjoy learning about the reactive
programming paradigm in Reactive Programming with Kotlin.
If you have any questions or comments as you work through this book, please
stop by our forums at https://forums.raywenderlich.com and look for the
particular forum category for this book.
Thank you again for purchasing this book. Your continued support is what
makes the books, tutorials, videos and other things we do at raywenderlich.com
possible. We truly appreciate it!
464
Functional Programming in Kotlin by Tutorials
A Appendix A: Chapter 1
Exercise Solutions
Written by Massimo Carli
Exercise 1.1
Implement the function sumInRange that sums the values in a List<String>
within a given interval. The signature is:
4. Use filter again, passing a lambda that checks whether the value is in the
range you pass in as input.
5. Invoke sum .
fun main() {
println(sumInRange(listOf("1", "10", "a", "7", "ad2", "3"),
1..5))
}
Getting:
465
Appendix A: Chapter 1
Functional Programming in Kotlin by Tutorials Exercise Solutions
This is the sum of the values in List<String> that are valid Int s and in the
range 1..5 .
Exercise 1.2
Implement chrono , which accepts a function of type () -> Unit as input and
returns the time spent to run it. The signature is:
fun main() {
val waitOneSec = { Thread.sleep(1000) } // 1
println(chrono(waitOneSec)) // 2
}
Here, you:
466
Appendix A: Chapter 1
Functional Programming in Kotlin by Tutorials Exercise Solutions
1005
467
Functional Programming in Kotlin by Tutorials
B Appendix B: Chapter 2
Exercise & Challenge
Solutions
Written by Massimo Carli
Exercise 2.1
Can you write an example of a function mapping distinct values in the domain to
non-distinct values in the range, like f(b) and f(c) in Figure 2.2?
f(a)
f(b)
f(c) a’
b’
Domain Range
Hint: Think of a possible way to group values you get as input. A very simple
example is to return a Boolean that tells if the value in input is positive or
not.
468
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
fun main() {
println(isEven(2))
println(isEven(-2))
println(isEven(12))
println(isEven(18))
println(isEven(19))
println(isEven(-3))
println(isEven(1))
println(isEven(-5))
}
true
true
true
true
false
false
false
false
Here, distinct values for the input are mapped to the same values in the output.
As you’ll see in the rest of the book, a function mapping any type A to a
Boolean is a Predicate . You can define all the predicates using the following
typealias:
Exercise 2.2
Can you write the inverse function of twice ? What are the domain and range
for the inverse function?
469
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
fun half(x: Int) = x / 2
The half type is still Fun<Int, Int> but, because of the division between
Int values, half isn’t invertible. This breaks the following required relation:
To prove that the last equation isn’t valid, try giving it some values for x :
x = 0 2 * (x / 2) == 2 * (0 / 2) == 2 * 0 = 0 == x
x = 1 2 * (x / 2) == 2 * (1 / 2) == 2 * 0 = 0 != x
x = 2 2 * (x / 2) == 2 * (2 / 2) == 2 * 1 = 2 == x
x = 3 2 * (x / 2) == 2 * (2 / 2) == 2 * 1 = 2 != x
As you can see, this happens because half isn’t invertible, so it can’t be the
inverse of twice . This is because if function f is the inverse of function g ,
then g must be the inverse of f . In this solution, half , seems to not work.
Seems because if you consider half to be the inverse of twice , you shouldn’t
invoke it using odd input values. This is because they wouldn’t be part of the
range of twice . To be rigorous, the type of twice is Fun<Int, EvenInt> ,
where EvenInt is the type of all the even integer numbers. In that case, the
inverse half would have the type Fun<EvenInt, Int> , and everything would
be fine.
But how can you represent the EvenInt type? All the concepts are purposely
being stressed a little bit here, but that’s the topic for Challenge 3!
Now, its type is Fun<Double, Double> , and the inverse function is:
470
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Exercise 2.3
Can you prove that using Set s as objects and “is a subset of” as morphisms results
in a category? In other words, a morphism from set A to set B would mean
that A is a subset of B . In that case, what are the initial and terminal objects?
Composition
Associativity
Identity
In this case, objects are sets, and morphisms define the relation of inclusion you
represent with the ⊆ symbol.
To prove composition, you need to prove that for every three sets A , B and
C , if A is a subset of B and B is a subset of C , then it’s also true that A is a
subset of C . Visualizing the relation with a Venn diagram, like in Figure 2a.1,
helps to prove composition:
C
B
471
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
From the definition of category, to prove associativity, you need to prove that:
(h◦g)◦f = h◦(g◦f) .
C
B
f = A is a subset of B
g = B is a subset of C
h = C is a subset of D
(h◦g) = B is a subset of D
(g◦f) = A is a subset of C
(h◦g)◦f = A is a subset of D
h◦(g◦f) = A is a subset of D
Identity has a simple proof because each set contains itself, so A is a subset of
A .
This proves that sets and the morphism “is a subset of” create a category.
472
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
What about the initial and terminal objects? Again, the definition comes to the
rescue. The initial point is an object with outgoing arrows to all other objects in
the category. In terms of sets, what set is the subset of all the other sets?
The terminal object is an object with unique incoming morphisms from all
other objects in the category. What, then, is a set containing all the other sets? It
has a name: superset.
The problem, in this case, is that the superset isn’t easy to represent in practice.
Think of the set of all the subsets of integer values. This doesn’t exist because,
for any candidate you find, there’s always another one containing it with some
other integer values not included in the initial candidate. For this reason, the
category of sets and the morphism “is a subset of” doesn’t have a terminal
object. Categories using some kind of ordering relation like the one in this
exercise don’t have terminal objects.
Exercise 2.4
In this chapter, you defined after , which allows you to write expressions like:
Can you write compose instead, which would allow you to implement the same
expression as:
473
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
In the body, you invoke the f receiver first and then pass the result to the
function g .
To test compose , use the following code with twice and format , which you
created in the previous exercises.
fun main() {
val f: Fun<Int, Int> = ::twice
val g: Fun<Int, String> = ::format
val formatTwice = f compose g // HERE
println(formatTwice(37))
}
Exercise 2.5
Can you write an example of an isomorphic function f and its inverse g and
prove they always compose to identity ?
To prove addOne and removeOne are the inverse of each other, you need to
prove that:
(x - 1) + 1 = 1 + (x - 1) = x
This is identity, so addOne and removeOne are the inverse of each other.
They’re both isomorphic functions.
474
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Challenge 1 solution
A Set is something more than a bunch of things because it has some
structure. An object can be in the set or not. If an object is in the Set , it must
be unique. You can’t have the same object in a Set twice.
The type for a function describing a Set is then Fun<A, Boolean> . Boolean
is an interesting type because you associate it with a set with just two elements:
true or false . A function of type Fun<A, Boolean> is a predicate, and you
can add the following definition to the Aliases.kt file in the project for this
chapter:
fun main() {
println(" 0 is even? ${evenIntSet(0)}")
println(" 9 is even? ${evenIntSet(-9)}")
println(" 10 is even? ${evenIntSet(10)}")
println(" 3 is even? ${evenIntSet(3)}")
}
0 is even? true
9 is even? false
10 is even? true
3 is even? false
Representing a set with a function allows you to check if a value is in the set or
not. To actually print all the values in the set, you need to scan the whole
domain and print the ones whose predicate function returns true .
(Int.MIN_VALUE..Int.MAX_VALUE)
.filter(evenIntSet)
.forEach { println(it) }
475
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Challenge 2 solution
Suppose you have the following functions for two different sets:
It’s interesting to note how the union and intersection sets are also
Predicate<A> , and therefore functions of the same type as the ones you had as
parameters.
val oddMultipleOf37Union =
union(oddIntSet, multipleOf37)
val oddMultipleOf37Intersection =
476
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
intersection(oddIntSet, multipleOf37)
What’s the domain and the range for this function? When you invoke
oneOver(0) , you get an exception.
How can you be sure you only pass values in the domain as an input parameter?
Challenge 3 solution
You probably know from your math in school that the function 1/x doesn’t exist
for x = 0. This means that the domain of oneOver is the set represented by all
the Int values without 0 .
The question now is: How would you represent the type of all the Int values
without 0? If the related type was NonZeroInt , the previous function would
become:
And, as said earlier, it would return a value for every input in its domain.
@JvmInline
value class NonZeroInt private constructor(val value: Int) {
companion object {
operator fun invoke(value: Int): NonZeroInt? {
return when (value) {
0 -> null
477
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
else -> NonZeroInt(value)
}
}
}
}
In this case, you can create a NonZeroInt only using a value that isn’t 0.
However, you have a problem. Try running the following code to understand
what the problem is:
fun main() {
println("1/3 = ${oneOver(NonZeroInt(3))}") // ERROR
}
fun main() {
println("1/3 = ${oneOver(NonZeroInt(3)!!)}") // COMPILES
}
A better idea is moving the error to the creation of the NonZeroInt object itself,
replacing the previous implementation of NonZeroInt with:
@JvmInline
value class NonZeroInt(val value: Int) {
init {
require(value != 0) { "O is not a value for this type!" }
}
}
println("1/3 = ${oneOver(NonZeroInt(3))}")
478
Appendix B: Chapter 2
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
When you run, you get the following output:
1/3 = 0.3333333333333333
println("1/3 = ${oneOver(NonZeroInt(0))}")
In both cases, as you’ll learn in the following chapters, this isn’t a very
functional way to handle this problem. One more reason to keep reading this
book! :]
479
Functional Programming in Kotlin by Tutorials
C Appendix C: Chapter 3
Exercise & Challenge
Solutions
Written by Massimo Carli
Exercise 3.1
Is inc a pure function?
var count = 0
fun main() {
println(inc(1))
println(inc(1))
println(inc(1))
println(inc(1))
}
Here, you invoke inc with the same value in input, but you get different values
in output, as you can see in the following logs:
2
3
4
5
Exercise 3.2
Is inc2 a pure function?
val count = 0
480
Appendix C: Chapter 3
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Exercise 3.3
Is expr3 :
val expr3 = 42
val (a1, a2) = expr3 to expr3
fun main() {
// Expr3 is referentially transparent, as you can see here
val expr3 = { 42 }
val (a1, a2) = expr3() to expr3()
val expr3Eval = expr3()
val (a1Eval, a2Eval) = expr3Eval to expr3Eval
assertOrThrow("expr3 is not RT") {
a1 == a1Eval && a2 == a2Eval
}
}
Exercise 3.4
Suppose you have the following code:
481
Appendix C: Chapter 3
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
val CounterIterator = object : Iterator<Int> {
fun main() {
// Expr4 is not referentially transparent, as you can see here
val expr4 = { CounterIterator.next() }
val (a1, a2) = expr4() to expr4()
val expr4Eval = expr4()
val (a1Eval, a2Eval) = expr4Eval to expr4Eval
assertOrThrow("expr4 is not RT") {
a1 == a1Eval && a2 == a2Eval
}
}
Every time you evaluate expr4 , you change the internal state of
CounterIterator , which is part of the external universe and, for the same
reason, you get a different output value.
Exercise 3.5
The Writer<A, B> data type you defined earlier is a very important concept in
functional programming. If you use types as objects and the functions
Writer<A, B> as morphisms, you get a very special category: the Kleisli
category.
482
Appendix C: Chapter 3
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Can you prove that by using types as objects and Writer<A, B> as morphisms,
you get a category?
1. Composition
2. Associativity
3. Identity
In this exercise, you basically need to prove these rules for every type A and
every function you represent as Writer<A, B> .
Proving composition
In this case, you have to prove that for every morphism f from the objects A
to B , and g from B to C , there’s always a morphism g◦f from A to C ,
which is the composition of f with g .
483
Appendix C: Chapter 3
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Proving associativity
To prove associativity for Writer<A, B> , you basically need to prove that:
Where:
f: Writer<A, B>
g: Writer<B, C>
h: Writer<C, D>
A possible way of doing this is applying the substitution model, but there’s a
simpler way. Look at how composition works in the previous paragraph, and
you’ll notice that associativity is true if it’s true for both the first and
second properties of the resulting Pair s of Writer s. For first , you’re
applying the normal composition of functions Fun<A, B> . For second , you’re
basically concatenating String s. Because you already know that associativity
is true for function Fun<A, B> , you basically need to also prove that String
concatenation is associative. Given three String s — str1 , str2 and str3 —
you can easily prove that:
This proves that, using the composition you defined in the previous paragraph,
associativity for Writer<A, B> is also true.
Proving identity
In this case, you need to prove that for every type A , there’s always a morphism
Writer<A, A> called identity id<A> , such that, for every f of type
Writer<A, B> the following is true:
f after id == id after f == f
484
Appendix C: Chapter 3
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
This function suggests what the identity might be. Just imagine starting from
the identity for Fun<A, B> , which is:
And apply liftW() using the empty String . What you’ll get is simply:
Now, you need to prove that, whatever f of type Writer<A, B> is, the
following rule is true:
f after id == id after f == f
To prove this, you need to start with the implementation of composition for
Writer<A, B> . Then, use id<A> both as the receiver and as the parameter
w .
f after id
You can write the definition of composition assuming that w is id<A> , getting:
In the above:
str = ""
b = a
c = f(b) = f(a)
"$str\n$str2\n" = "$str2\n"
485
Appendix C: Chapter 3
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
f(a) to "$str2\n"
Now, you need to prove that you get the same result starting from:
id after f
Which becomes:
f(a) to "$str\n"
Just note that in this case, the decoration of f is str and not str2 , like in
the previous case.
This proves that by using types as objects and Writer<A, B> as morphisms,
you get a category that’s actually very important: the Kleisli category!
var count = 0
fun inc3(x: Int): Int {
val result = x + ++count + 1
println("Res: $result") // WHAT IF YOU REMOVE THIS?
486
Appendix C: Chapter 3
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
--count
return result
}
Challenge 1 solution
The answer, in this case, isn’t so obvious. Every time you invoke inc3 , you
calculate a result incrementing count , a global variable. You then print the
result and decrement the global variable count before returning the result.
println makes this function impure.
But what if you remove the println ? Is the function still impure? The function
is still impure because it changes the values of count while executing. If you
invoke another function in another thread accessing the same count variable,
a race condition might cause some unexpected results.
Challenge 2 solution
This function always provides the same output, Unit , for the same input. The
problem is that it logs messages, so it has side effects. Because of this, output
is not a pure function.
Challenge 3 solution
This function doesn’t provide the same output for the same input values
because of the Random component. What about side effects? Even if it’s not
directly visible, this function has a side effect: It changes the state of the
Random object.
487
Functional Programming in Kotlin by Tutorials
D Appendix D: Chapter 4
Exercise & Challenge
Solutions
Written by Massimo Carli
Exercise 4.1
Can you write a lambda expression that calculates the distance between two
points given their coordinates, x1, y1 and x2, y2 ? The formula for the
distance between two points is distance = √(x2−x1)²+(y2−y1)² .
1. Define a lambda expression with four Double parameters for each set of
coordinates.
2. Calculate the square of the distances between coordinates x and y .
3. Produce the final return result with the last expression of the lambda.
You can get the same result in other ways. Here’s one using the Point
typealias:
488
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
You can then test your lambdas with the following code:
fun main() {
println(distanceLambda(0.0, 0.0, 1.0, 1.0))
println(distanceLambdaWithPairs(0.0 to 0.0, 1.0 to 1.0))
}
1.4142135623730951
1.4142135623730951
Exercise 4.2
What’s the type for the lambda expression you wrote in Exercise 4.1?
Or:
Exercise 4.3
What are the types of the following lambda expressions?
val emptyLambda = {} // 1
val helloWorldLambda = { "Hello World!" } // 2
val helloLambda = { name: String -> "Hello $name!" } // 3
val nothingLambda = { TODO("Do exercise 4.3!") } // 4
489
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
typealias AbsurdType = (Nothing) -> Nothing
val emptyLambda = {}
This is a lambda expression with no input parameters and no return value. Well,
that’s not quite true in Kotlin. The expression is actually returning something:
Unit . Its type is then:
Is:
Which is:
In this case, the return type is Nothing . The return type would also be
Nothing if you throw an exception. The type nothingLambda is then:
490
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
fun main() {
absurd(TODO("Invoked?"))
}
Because Kotlin uses strict evaluation, it evaluates the expression you pass as a
parameter of absurd before the absurd itself. In this case, TODO doesn’t
complete, so the absurd will never be invoked. The same happens with:
fun main() {
absurd(throw Exception("Invoked?"))
}
Exercise 4.4
Can you implement a function simulating the short-circuit and operator with
the following signature without using && ? In other words, can you replicate the
short-circuiting behavior of left && right :
Can you also write a test to prove that right evaluates only if left is false?
491
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
In Kotlin, if is an expression, so you can implement shortCircuitAnd like
this:
fun shortCircuitAnd(
left: () -> Boolean,
right: () -> Boolean
): Boolean = if (left()) {
right()
} else {
false
}
fun main() {
val inputValue = 2
shortCircuitAnd(
left = { println("LeftEvaluated!"); inputValue > 3 },
right = { println("RightEvaluated!"); inputValue < 10 },
)
}
LeftEvaluated!
LeftEvaluated!
RightEvaluated!
Exercise 4.5
Can you implement the function myLazy with the following signature, which
allows you to pass in a lambda expression and execute it just once?
492
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
It’s also important to note that A has a constraint, which makes it non-null.
This makes the exercise a little bit easier, because you can write something like:
1. Use result as a local variable that tells you whether the lambda expression
fn has been evaluated, and the value of type A can’t be null because of the
constraint.
2. Return a lambda of the same type, () -> A , as the input parameter.
fun main() {
val myLazy = myLazy { println("I'm very lazy!"); 10 }
3.times {
println(myLazy())
}
}
This proves the lambda expression is actually evaluated only once, and the
result is simply reused.
You can remove the not nullability constraint in Challenge 4.1. See you there! :]
493
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Exercise 4.6
Create a function fibo returning the values of a Fibonacci sequence.
Remember, every value in a Fibonacci sequence is the sum of the previous two
elements. The first two elements are 0 and 1 . The first values are, then:
0 1 1 2 3 5 8 13 21 ...
fun main() {
val fiboSeq = fibo()
10.times {
print("${fiboSeq()} ")
}
}
0 1 1 2 3 5 8 13 21 34
Challenge 4.1
494
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
fun main() {
val myNullableLazy: () -> Int? =
myNullableLazy { println("I'm nullable lazy!"); null }
3.times {
println(myNullableLazy())
}
}
Challenge 4.2
You might be aware of Euler’s number e. It’s a mathematical constant of huge
importance that you can calculate in very different ways. It’s an irrational
number like pi that can’t be represented in the form n/m . Here you’re not
required to know what it is, but you can use the following formula:
495
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Can you create a sequence that provides the sum of the n terms of the given
formula?
return {
currentSum += 1.0 / factorial(n++, 1).toDouble() // 3
currentSum
}
}
This basically translates the formula in Figure 4.2 into code. Here, you:
fun main() {
val e = e()
10.times {
println(e())
}
}
496
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
2.0
2.5
2.6666666666666665
2.708333333333333
2.7166666666666663
2.7180555555555554
2.7182539682539684
2.71827876984127
2.7182815255731922
2.7182818011463845
Why not also make it a sequence? In this case, you can write:
You can then write and run the following code when you use fastE instead of
e :
fun main() {
val e = fastE() // HERE
10.times {
println(e())
}
}
497
Appendix D: Chapter 4
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
2.0
2.5
2.6666666666666665
2.708333333333333
2.7166666666666663
2.7180555555555554
2.7182539682539684
2.71827876984127
2.7182815255731922
2.7182818011463845
498
Functional Programming in Kotlin by Tutorials
E Appendix E: Chapter 5
Exercise & Challenge
Solutions
Written by Massimo Carli
Exercise 5.1
Kotlin provides you with first , which returns the first element of
Iterable<T> for which a predicate you provide as an input evaluates to true .
Remember that Iterable<T> is the abstraction of all the collections providing
an Iterator<T> implementation.
Iterator<T> allows you to iterate over all the elements of a collection in a way
that doesn’t depend on the collection implementation itself:
499
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
As mentioned, Kotlin doesn’t allow you to override the extension function on
Iterable<T> so, for this exercise, you need to implement first on
Array<T> . A possible solution is:
fun main() {
val input = arrayOf(1, 2, 3, 4, 5)
println(input.first {
it > 3 // 1
})
println(input.first {
it > 10 // 2
})
}
4
Exception in thread "main" java.util.NoSuchElementException: Array
contains no element matching the predicate.
Note how:
500
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
What if you don’t want to throw an exception but implement first in a more
functional way without NoSuchElementException as a side effect?
The code isn’t very different from the first one. Here, you:
fun main() {
val input = arrayOf(1, 2, 3, 4, 5)
println(input.first {
it > 3
})
println(input.firstOrNull { // HERE
it > 10
})
}
In the previous code, you use firstOrNull instead of first . Run this code,
and you get:
4
null
Exercise 5.2
The command pattern is another important design pattern that defines
abstractions like Command and CommandExecutor . Command abstracts every
possible operation that CommandExecutor can run. In other words, a Command
represents a task and you can pass a Command to a CommandExecutor to run it.
How would you represent them in a functional way?
Optionally, can you also provide a way to “redo” the most recent Command ?
501
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
fun redo() { // 4
commandHistory.lastOrNull()?.let {
it()
}
}
}
Usually, the command pattern also allows you to undo Command s, but that’s out
of the scope of this book.
502
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Exercise 5.3
Can you implement the following Reader interface as a functional interface?
How would you test it?
interface Reader {
fun readChar(): Char?
fun readString(): String {
TODO("Call readChar() until it returns null")
}
}
So far, so good! But how do you test it? Does making Reader a functional
interface help? Actually, it doesn’t.
Here, you:
503
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
as an input parameter.
2. Initialize pos to 0 , which is the position of the first Char you want to
return from readChar .
3. Check if pos is in the boundary of the String provided as input. If so, you
return the Char in it and increment pos . Otherwise, you return null .
fun main() {
val input = MyReader("This is a String!")
println(input.readString())
}
Getting:
This is a String!
The initial question remains: What advantage do you have by making Reader a
functional interface? It gives you two advantages:
You could test the first case with the following code:
fun main() {
val inputString = "This is a String!"
var pos = 0
val input = Reader {
if (pos < inputString.length) inputString[pos++] else null
}
println(input.readString())
}
In this case, the Reader implementation captures the value of pos , which
basically represents its own state. What if you had the following code instead?
fun main() {
val inputString = "This is a String!"
var pos = 0
val input = Reader {
if (pos < inputString.length) inputString[pos++] else null
}
val input2 = Reader {
if (pos < inputString.length) inputString[pos++] else null
504
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
}
println(input.readString())
println(input2.readString())
}
Only the first println would actually print something. To make things work,
you’d need a second variable for the state of Reader for input2 , like this:
fun main() {
val inputString = "This is a String!"
var pos = 0
var pos2 = 0
val input = Reader {
if (pos < inputString.length) inputString[pos++] else null
}
val input2 = Reader {
if (pos2 < inputString.length) inputString[pos2++] else null
}
println(input.readString())
println(input2.readString())
}
You’d have the same problem, even if you pass Reader as an input parameter
of another function, like this:
fun main() {
var pos = 0
val inputString = "This is a String!"
consumeReader({
if (pos < inputString.length) inputString[pos++] else null
})
}
The only advantage here is that you don’t need to specify the Reader type
before the lambda because the Kotlin compiler infers it for you.
So, what’s the lesson here? As you learned in the chapter, typealias allows
you to give a name to an existing type. In the case of function types, you can
assume they already exist somewhere, and typealias is a tool to reduce the
quantity of code you have to write, especially for generic types. Types like (T)->
Boolean , (T,T) -> T and so on already exist, even if you don’t declare them
explicitly.
The type you define using a functional interface is a new type, and the
implementations must specify the name explicitly. They don’t exist before that,
505
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
even if they basically define an existing type. As proved in the chapter, consider
the functional interface:
As a last note, remember that typealias es aren’t visible from Java, but
functional interfaces are.
Exercise 5.4
Implement an extension function isEqualsPredicate on the generic type T
that returns a predicate that tests if a given value is equal to the same T . The
signature should be the following:
How would the same function be different if you use the following functional
interface?
fun main() {
listOf(1, 2, 3, 4, 4, 5, 6, 7, 8, 8)
.filter(4.isEqualsPredicate())
.forEach(::println)
}
506
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
4
4
Note how the name of the extension function isn’t isEqualsPredicate . This
avoids conflicts with the previous one because they’re both acting on the same
receiver, T , and not because of the different return type.
Exercise 5.5
Can you implement the same logic for implementing the example in the
Imperative vs. declarative approach section using the definitions of
Predicate1<T> and filterWithPredicate ? Given a list of email addresses,
you need to:
fun main() {
emails
.filterWithPredicate(isValidEmail and isLongerThan(10)) // 3
.take(5) // 4
.forEach(::println) // 5
}
507
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
In this code, you:
email@emmmaail.com
mike@mcarli.it
first.second@ggg.com
test@test.co.uk
fp_is_great@funprog.com
Can you implement the function map for the Array<A> type with the following
signature?
fun main() {
val square = { a: Int -> a * a }
val toString = { a: Int -> "This is $a" }
arrayOf(1, 2, 3)
.map(square)
.forEach(::println)
arrayOf(1, 2, 3)
.map(toString)
.forEach(::println)
}
508
Appendix E: Chapter 5
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
You should get:
1
4
9
This is 1
This is 2
This is 3
inline fun <A, reified B> Array<A>.map(fn: (A) -> B): Array<B> =
Array(this.size) { fn(this[it]) }
This simply uses the constructor for Array to initialize all the values after
applying fn to all the elements in the original array.
Note the use of reified for the type B . This is because the Kotlin compiler
needs to retain some information about the type B to properly initialize
Array in output.
1
4
9
This is 1
This is 2
This is 3
Then, use it to return all the positive prime values in Array<Int> . A number is
prime if it’s not evenly divisible with any number other than 1 and itself.
You can implement all in many different ways. One of them is:
You leverage the existing filter , adapting Predicate1<T> to the type (T) ->
Boolean it requires. Then, you use toTypedArray() to make the List<T> an
Array<T> . This requires more type information and is the reason for the
reified keyword.
fun main() {
arrayOf(1, 45, 67, 78, 34, 56, 89, 121, 2, 11, 12, 13)
.all(isPrime)
.forEach(::println)
}
89
2
11
13
510
Functional Programming in Kotlin by Tutorials
F Appendix F: Chapter 6
Exercise & Challenge
Solutions
Written by Massimo Carli
Exercise 6.1
In this section, you learned it’s useful to avoid creating multiple instances of
immutable classes because they represent the same value. Given the following
immutable class Id :
How would you change it to prevent any client from creating multiple instances
of Id for the same id ?
fun main() {
val id1 = // Create Id for id = 1
val id2 = // Create Id for id = 1
val id3 = // Create Id for id = 2
val id4 = // Create Id for id = 2
println("${id1 === id2}")
println("${id1 === id2}")
println("${id1 === id3}")
println("${id3 === id4}")
}
You get:
true
true
false
true
511
Appendix F: Chapter 6
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
class is created. In this case, “controlling” means:
The last point is the one you’ll use to do the exercise. Consider, for instance, the
following code:
1. Define a class Id with a private constructor. With this, no client can directly
create an instance of Id — only a method of the same Id class can.
2. Create a companion object so you can invoke the factory method without an
instance of Id .
fun main() {
val id1 = Id.of(1)
val id2 = Id.of(1)
val id3 = Id.of(2)
512
Appendix F: Chapter 6
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
val id4 = Id.of(2)
println("${id1 === id2}")
println("${id1 === id2}")
println("${id1 === id3}")
println("${id3 === id4}")
}
true
true
false
true
Exercise 6.2
What happens if the Id class in Exercise 6.1 is a data class?
This compiles fine but, if you look carefully, IntelliJ gives you the warning in
Figure 6a.1:
Figure 6a.1: Private primary constructor is exposed via the generated 'copy()' method of a
'data' class.
This means that with data classes, you can always create a copy of a class
through the copy method. It’s also important to note how the value you get
with copy is an object with its own identity.
513
Appendix F: Chapter 6
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
fun main() {
val id1 = Id.of(1)
val id2 = id1.copy()
println("${id1 == id2}") // 1
println("${id1 === id2}") // 2
}
true
false
@NotNull
public final Id copy(int id) { // 2
return new Id(id);
}
return var0.copy(var1);
}
// ...
}
1. Id constructor is private .
2. copy invokes the Id constructor, making de facto public.
514
Appendix F: Chapter 6
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
The potential Kotlin value classes should solve these kinds of problems.
Exercise 6.3
The Tower of Hanoi is a classic example of a recursive function. It is a famous
game consisting of three rods and a set of n disks of different radii. At the
beginning, all the disks are stacked on the first rod. You need to move all the
disks from the first rod to the third, following some rules.
Each move consists of taking the top disk from one of the stacks and placing
it on top of another stack or on an empty rod.
No disk may be placed on top of a disk that’s smaller than it.
3. When moving disks from rod 1 to rod 3 , you can use rod 2 as an
intermediate step.
4. For moving n disks from rod 1 to rod 3 , you need to move n-1 disks
from rod 1 to rod 2 using rod 3 . Then, move the remaining from rod
1 to rod 3 . Finally, you move the remaining n-1 from rod 2 to rod 3
using rod 1 . You can see a visualization for this on Wikipedia.
515
Appendix F: Chapter 6
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Following these steps, you end up writing code like this:
In this code, you represent the number of disks with a number, as well as each
rod. The parameters include what disk you’re currently trying to move, the rod
you’re moving from, the rod you’re moving to, and the rod you’re using as the
intermediary.
You recursively start by moving the disks above the current one from the from
rod to the using rod. You can then move them from the using rod to the to
rod and work your way down the stack.
fun main() {
moveDisk(disks = 4, from = 1, to = 3, using = 2)
}
Moving 1 from 1 to 2
Moving 2 from 1 to 3
Moving 1 from 2 to 3
Moving 3 from 1 to 2
Moving 1 from 3 to 1
Moving 2 from 3 to 2
Moving 1 from 1 to 2
Moving 4 from 1 to 3
Moving 1 from 2 to 3
Moving 2 from 2 to 1
Moving 1 from 3 to 1
Moving 3 from 2 to 3
Moving 1 from 1 to 2
Moving 2 from 1 to 3
Moving 1 from 2 to 3
You can change the number of disks as well and watch the result.
Exercise 6.4
Tail-recursive functions usually provide better performance. Can you prove this
using the chrono function in Util.kt?
516
Appendix F: Chapter 6
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
/** Utility that measures the time for executing a lambda N times
*/
fun chrono(times: Int = 1, fn: () -> Unit): Long {
val start = System.nanoTime()
(1..times).forEach({ fn() })
return System.nanoTime() - start
}
tailrec fun tailRecFactorial(n: Int, fact: Int = 1): Int = when (n)
{ // 2
1 -> fact
else -> tailRecFactorial(n - 1, n * fact)
}
These are:
For the sake of this exercise, you also create noTailRecFactorial as a version
of tailRecFactorial but without the tailrec keyword:
fun main() {
val times = 1000000
println("recursiveFactorial ${chrono(times) {
recursiveFactorial(50)
}}") // 1
println("tailRecFactorial ${chrono(times) {
tailRecFactorial(50)
517
Appendix F: Chapter 6
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
}}") // 2
println("noTailRecFactorial ${chrono(times) {
noTailRecFactorial(50)
}}") // 3
}
recursiveFactorial 92446751
tailRecFactorial 8587841
noTailRecFactorial 50125777
Of course, your values will probably be different, but what matters here is the
comparison. As you can see:
This suggests that tail-recursive implementations, when possible, are the best in
terms of performance.
fun main() {
val list = listOf(1, 5, 10, 12, 34, 55, 80, 23, 35, 12, 80)
println(recAddMulti5(list))
}
518
Appendix F: Chapter 6
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
fun main() {
println(chrono {
noTailRecFib(40) // 1
})
println(chrono {
tailRecFib(40) // 2
})
}
527457813 // 1
12316 // 2
519
Functional Programming in Kotlin by Tutorials
G Appendix G: Chapter 7
Exercise & Challenge
Solutions
Written by Massimo Carli
Exercise 7.1
Implement the extension function isEmpty() , which returns true if
FList<T> is empty and false otherwise.
Here, you just return true if the size is 0 and false otherwise.
fun main() {
println(FList.empty<Int>().isEmpty())
println(FList.of(1).isEmpty())
println(FList.of(1, 2, 3).isEmpty())
}
520
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
true
false
false
Exercise 7.2
Implement the extension function tail() , which returns the tail of a given
FList<T> .
fun main() {
println(FList.empty<Int>().tail())
println(FList.of(1).tail())
println(FList.of(1, 2, 3).tail())
}
com.raywenderlich.fp.Nil@27c170f0
com.raywenderlich.fp.Nil@27c170f0
FCons(head=2, tail=FCons(head=3,
tail=com.raywenderlich.fp.Nil@27c170f0))
Exercise 7.3
Kotlin provides forEachIndexed for the Iterable<T> interface, which accepts
as input a lambda of type (Int, T) -> Unit . The first Int parameter is the
index of the item T in the collection. To test forEachIndexed , run the code:
521
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
listOf("a", "b", "c").forEachIndexed { index, item ->
println("$index $item")
}
0 a
1 b
2 c
loop() // 6
}
3. Do nothing if the receiver is Nil because you completed the iteration on the
list.
4. Evaluate the input lambda fn , passing the current index and the current
element otherwise.
5. Invoke the same forEachIndexed on tail , passing the next index value as
input.
6. Invoke loop , using its default input parameter, 0 , to start the iteration.
522
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
fun main() {
FList.of("a", "b", "c").forEachIndexed { index, item ->
println("$index $item")
}
}
0 a
1 b
2 c
Exercise 7.4
Another option to implement forEachIndexed is to make FList<T> an
Iterable<T> . How would you do that? To make all the code coexist in the same
codebase, call the Iterable<T> version IFList<T> with INil and
ICons<T> .
companion object {
@JvmStatic
fun <T> of(vararg items: T): IFList<T> {
val tail = items.sliceArray(1 until items.size)
return if (items.isEmpty()) {
empty()
} else {
ICons(items[0], of(*tail))
}
}
@JvmStatic
fun <T> empty(): IFList<T> = INil
}
}
523
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
}
In this code, after copying the content of FList.kt and renaming FList<T> to
IFList<T> , Nil to INil and FCons<T> to ICons<T> , you:
4. Define current as the current state of the iterator, pointing initially to the
ICons<T> receiver itself.
fun main() {
IFList.of(1, 2, 3).forEach {
println(it)
}
}
524
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
1
2
3
Exercise 7.5
Implement addHead , which adds a new element at the head of an existing
FList<T> .
You just create a new FCons<T> object by passing the new value as head and
using the current receiver as the tail .
fun main() {
val initialList = FList.of(1, 2)
val addedList = initialList.addHead(0)
initialList.forEach {
print("$it ")
}
println()
addedList.forEach {
print("$it ")
}
}
1 2
0 1 2
Exercise 7.6
Kotlin defines the take function on Iterable<T> that allows you to keep a
525
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
given number of elements.
fun main() {
listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.take(3)
.forEach { print("$it ") }
You’d get:
1 2 3
3. Check the parameter n that indicates how many elements you have to take.
If this isn’t 0 , you return a new FList<T> containing the head and, as the
tail , what you get invoking take for n - 1 elements.
fun main() {
FList.of(1, 2, 3, 4, 5)
.take(0) // 1
.forEach { print("$it ") }
println()
FList.of(1, 2, 3, 4, 5)
526
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
.take(1) // 2
.forEach { print("$it ") }
println()
FList.of(1, 2, 3, 4, 5)
.take(5) // 3
.forEach { print("$it ") }
println()
FList.of(1, 2, 3, 4, 5)
.take(6) // 4
.forEach { print("$it ") }
}
// 1
1 // 2
1 2 3 4 5 // 3
1 2 3 4 5 // 4
1. take(0) .
2. take(1) .
Exercise 7.7
Kotlin defines the takeLast function on Iterable<T> that allows you to keep
a given number of elements at the end of the collection. For instance, running
the following code:
fun main() {
listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.takeLast(3)
.forEach { print("$it ") }
You’d get:
8 9 10
527
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
implementation could be the following:
Here, you:
3. Check the value of n . If it’s greater than the current size , you return the
result of invoking takeLast on the tail . This is because it means you still
have elements to skip at the beginning of the list.
4. Return a new FCons<T> using the current head and tail if the size is
equal to or smaller than n .
fun main() {
(0..6).forEach {
println("takeLast $it")
FList.of(1, 2, 3, 4, 5)
.takeLast(it)
.forEach { print("$it ") }
println()
}
}
It allows you to test takeLast with different values for the size of the input. You
get:
takeLast 0
takeLast 1
5
takeLast 2
4 5
takeLast 3
3 4 5
takeLast 4
2 3 4 5
528
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
takeLast 5
1 2 3 4 5
takeLast 6
1 2 3 4 5
The previous code is simple, but it uses the function size you implemented in
the chapter, which has complexity O(N). This makes the complexity of
takeList O(N^2). The usual question is: Can you do better? Of course you can!
Following this idea, you could implement the skip function like this:
Here, you:
fun main() {
// ...
(0..6).forEach {
println("Skipping $it")
FList.of(1, 2, 3, 4, 5)
.skip(it)
.forEach { print("$it ") }
529
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
println()
}
}
You get:
Skipping 0
1 2 3 4 5
Skipping 1
2 3 4 5
Skipping 2
3 4 5
Skipping 3
4 5
Skipping 4
5
Skipping 5
Skipping 6
Here, you:
2. Invoke size to get the length of the receiver, and invoke skip to skip the
values in excess.
fun main() {
(0..6).forEach {
println("takeLast2 $it")
FList.of(1, 2, 3, 4, 5)
.takeLast2(it)
.forEach { print("$it ") }
println()
}
}
530
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
takeLast2 0
takeLast2 1
5
takeLast2 2
4 5
takeLast2 3
3 4 5
takeLast2 4
2 3 4 5
takeLast2 5
1 2 3 4 5
takeLast2 6
1 2 3 4 5
Because you invoke size only once, the complexity of takeLast2 is O(N) .
Challenge 7.1
Kotlin provides the functions first and last as extension functions of
List<T> , providing, if available, the first and last elements. Can you implement
the same for FList<T> ?
The implementation of last is also simple if you use the functions skip and
size that you implemented earlier.
Run the following code to get the value of first and last in some specific
edge cases:
fun main() {
println(FList.empty<Int>().first())
println(FList.empty<Int>().last())
println(FList.of(1).first())
println(FList.of(1).last())
println(FList.of(1, 2).first())
println(FList.of(1, 2).last())
}
531
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
null
null
1
1
1
2
Challenge 7.2
Kotlin provides an overload of first for Iterable<T> that provides the first
element that evaluates a given Predicate<T> as true . It also provides an
overload of last for List<T> that provides the last element that evaluates a
given Predicate<T> as true . Can you implement firstWhen and lastWhen
for FList<T> with the same behavior?
Here, you just use filter to get the FList<T> of all the values for the given
predicate, and then you take the first element.
You might notice that in this case filter creates a complete FList<T> of all
the values that evaluate the predicate as true , but you just need the first. A
possible better implementation is the following:
In this code, you search for the first element that evaluates the predicate to
true . You do this by testing the head and continue to the tail if it evaluates
to false .
532
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Here, the only difference is that you take the last element of the one you got,
invoking filter first.
Note how a fast version of lastWhen doesn’t make sense because you always
need to evaluate all the elements in FList<T> to find the last one.
fun main() {
val isEven: Predicate<Int> = { a: Int -> a % 2 == 0 }
println(FList.of(1, 2, 3, 4, 5, 6).firstWhen(isEven))
println(FList.of(1, 2, 3, 4, 5, 6).lastWhen(isEven))
println(FList.of(1, 2, 3, 4, 5, 6).fastFirstWhen(isEven))
}
You get:
2
6
2
Challenge 7.3
Implement the function get that returns the element at a given position i in
FList<T> . For instance, with this code:
fun main() {
println(FList.of(1,2,3,4,5).get(2))
}
You get:
533
Appendix G: Chapter 7
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
following:
You basically skip i values and take the head . If the head doesn’t exist, you
throw an ArrayIndexOutOfBoundsException . This allows you to run the
following code:
fun main() {
val list = FList.of(1, 2, 3)
println(list.get(0))
println(list.get(1))
println(list.get(2))
println(list.get(3))
}
And get:
1
2
3
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
To make things fancier, just use the operator keyword like this:
fun main() {
val list = FList.of(1, 2, 3)
println(list[0])
println(list[1])
println(list[2])
println(list[3])
}
534
Functional Programming in Kotlin by Tutorials
H Appendix H: Chapter 8
Exercise & Challenge
Solutions
Written by Massimo Carli
Exercise 8.1
In this chapter, you implemented the generic curry function that basically
maps a function of type (A, B) -> C in a function of type (A) -> (B) -> C .
Can you now implement the uncurry function, which does the inverse? It’s a
function that maps a function of type (A) -> (B) -> C in a function of type
(A, B) -> C .
fun <A, B, C> ((A) -> (B) -> C).uncurry(): (A, B) -> C =
{ a: A, b: B ->
this(a)(b)
}
This is an extension function of the (A) -> (B) -> C type that returns a
function of two input parameters, a and b , of type A and B , respectively. In
the body, you just invoke the receiver function first with a and then the
resulting function with b .
It’s interesting to note how, if you apply uncurry to the curry version of a
function, you get the function itself. To prove this, run the following code:
fun main() {
val sum = { a: Int, b: Int -> a + b }
println(sum(2, 3))
val sum2 = sum.curry().uncurry()
println(sum2(2, 3))
}
5
5
535
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Exercise 8.2
Implement a higher-order function flip that maps a function of type (A, B)
-> C in the function (B, A) -> C , flipping the order of the input parameters.
In the body, you just invoke the receiver, passing the parameters in the right
order.
As a first example of the usage of this function, you can use the following:
fun main() {
val flippedAppend = ::append.flip() // 1
println(append("First", "Second")) // 2
println(flippedAppend("First", "Second")) // 3
}
2. Print the result of append , passing "First" and "Second" as input values.
3. Print the result of flippedAppend with the same parameters in the same
order.
536
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
You’ll get:
First Second
Second First
Sometimes, using flip with curry is useful. Consider, for instance, the
following function:
3. Invoke fn .
fun main() {
// ...
runDelayed({
println("Delayed")
}, 1000)
}
You’ll see the program wait one second and then print Delayed . This is code
you can improve because you pass the lambda expression as the first parameter
and the interval as the second. Of course, you could use named parameters, but
you can do something better instead.
Just run the following code:
fun main() {
// ...
val runDelayed1Second =
::runDelayed.flip() // 1
.curry() // 2
.invoke(1000L) // 3
runDelayed1Second { // 4
println("Delayed")
}
}
537
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
2. Invoke curry() , getting a function of type (Long) -> (() -> Unit) ->
Unit .
3. Invoke the curried function with 1000L as an input parameter for the delay.
This makes (() -> Unit) -> Unit the type of runDelayed1Second .
4. Use runDelayed1Second , passing the lambda expression you want to run,
but delayed by 1 second.
Using composition in this way makes the code more reusable and simpler to
write.
Exercise 8.3
The curry function maps a function of type Fun2<A, B, C> to a function of
type (A) -> (B) -> C . How would you define an overload of curry for
functions of three, four, five or, in general, n parameters?
You can also do the same for the output types for the curry functions, like
this:
typealias Chain3<I1, I2, I3, O> = (I1) -> (I2) -> (I3) -> O
typealias Chain4<I1, I2, I3, I4, O> =
(I1) -> (I2) -> (I3) -> (I4) -> O
typealias Chain5<I1, I2, I3, I4, I5, O> =
(I1) -> (I2) -> (I3) -> (I4) -> (I5) -> O
538
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
In the case of three parameters, you can then write the following curry
overload:
How can you consider a function with three parameters as a function with two
parameters returning another function? Basically, you consider the type:
As the following:
This allows you to reuse the curry overload you implemented for N-1
parameters for a function of N parameters. Now, you can do the same for
functions with four and five parameters, like this:
fun <I1, I2, I3, I4, O> Fun4<I1, I2, I3, I4, O>.curry():
Chain4<I1, I2, I3, I4, O> = { i1: I1, i2: I2, i3: I3 ->
{ i4: I4 ->
this(i1, i2, i3, i4)
}
}.curry()
And:
fun main() {
val sum = { a: Int, b: Int, c: Int, d: Int, e: Int ->
a + b + c + d + e // 1
}
val curriedSum = sum.curry() // 2
println(curriedSum(1)(2)(3)(4)(5)) // 3
}
539
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
1. Implement a simple function, sum , that calculates the sum of the five input
parameters.
3. Invoke curriedSum and print the result. Note how you pass the input
parameters using () .
15
curriedSum(1)(2)(3)(4)(5)
fun main() {
val sum = { a: Int, b: Int, c: Int, d: Int, e: Int ->
a + b + c + d + e
}
val curriedSum = sum.curry()
val result = 5 pipe 4 pipe 3 pipe 2 pipe 1 pipe curriedSum //
HERE
println(result)
println(curriedSum(1)(2)(3)(4)(5))
}
Unfortunately, this code doesn’t compile. The reason is the associativity priority
between the pipe infix functions, which is left to right. This means that the
compiler tries to execute 5 pipe 4 first, which doesn’t exist.
To use pipe , you need to use parentheses in another way, like this:
You basically just moved the same parentheses to another place. There’s a trick,
540
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
though. Simply add the following code:
The epip function is basically the pipe reversed, but it allows you to
completely remove parentheses. Just replace the previous code with the
following:
fun main() {
val sum = { a: Int, b: Int, c: Int, d: Int, e: Int ->
a + b + c + d + e
}
val curriedSum = sum.curry()
val result = curriedSum epip 1 epip 2 epip 3 epip 4 epip 5 //
HERE
println(result)
}
Feel free to play with these curry overloads and the flip function you
implemented in this exercise to change the order of your functions as you like.
Exercise 8.4
How would you apply the previous pattern for Array<T> ? Basically, you need a
way to compose functions of type:
Can you implement compose so that the following will compile and fun2 is
applied to all elements resulting from fun1 ?
541
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
returning an Array<B> . The second receives an input of type B and returns
an Array<C> .
The composition should then be something that gets an Array<B> from the
first function and applies the second function to all the elements. A possible
implementation is:
Now you can create your own example to test how this works. For instance,
write the following:
fun main() {
val counter = { a: Int -> Array(a) { it } } // 2
val fiboLength = { n: Int -> Array(n) { fibo(it) } } // 3
val counterFibo = counter compose fiboLength // 4
counterFibo(5).forEach { print("$it ") } // 5
}
542
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
Here, you:
1. Define a utility function, fibo , that returns the n th value in the Fibonacci
sequence.
1 1 1 1 1 2 1 1 2 3
To understand this output a bit better, walk through what it’s doing:
3. The results of each of these interations are combined into the final resulting
list that you see printed at the end.
In Chapter 12, “Monoids & Semigroups”, you’ll learn how to use a very
important function called fold . If you already know how to use it, a possible
alternate solution is:
As you’ll learn, to use fold , you need to define what it means for a type to be
composable. To test this implementation, just add and run this code:
543
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
fun main() {
// ...
val counterFiboWithFold = counter composeWithFold fiboLength
counterFiboWithFold(5).forEach { print("$it ") }
}
1 1 1 1 1 2 1 1 2 3
How would you implement compose for WithCallable<A, B> ? This is using
java.util.concurrent.Callable defined as:
interface Callable<V> {
@Throws(Exception::class)
fun call(): V
}
Challenge 1 solution
Following the same pattern you learned in the chapter, you can implement
compose like this:
Here, you:
4. Get the body of the returning Callable<C> invoking call on the receiver
544
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
and then call again on the Callable<B> you get in the first place.
fun main() {
val waitAndReturn = { a: Int -> // 1
Callable {
sleep(1000)
a
}
}
val comp = waitAndReturn compose waitAndReturn // 2
chronoMs {
comp(2).call() // 3
} pipe ::println
}
Here:
3. Using the chrono function in Util.kt , you check that by invoking call ,
you’re actually invoking the call on the WithCallable<A, B> you’re
composing.
2053
Note: Remember that sleep doesn’t allow you to wait a specific amount of
time but rather a minimum amount of time. This is because it guarantees
that the thread scheduler puts the current thread in a runnable state for the
time you pass as an input parameter. A thread in a runnable state is a
candidate to run, but this doesn’t mean it’ll run soon. This is also why the
previous output isn’t exactly 2000 but a little bit more.
val three = { 3 } // 1
545
Appendix H: Chapter 8
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
In this code:
They look like the same function, but they’re actually not. This is because you
need a Unit to invoke unitToThree . This also has consequences when you
compose. Consider the following code:
fun main() {
val double = { a: Int -> a * 2 } // 1
val comp2 = unitToThree compose double // 2 COMPILE
val comp1 = three compose double // 3 DOESN'T COMPILE
}
Here, you:
1. Define a simple double function.
The reason is that you don’t have any compose overload with the type () -> T
as a receiver. The type (Unit) -> T instead falls into Fun<A, B> .
Challenge 2 solution
The solution to this challenge is very simple. You just need to define the
following functions:
fun main() {
val double = { a: Int -> a * 2 }
val comp2 = unitToThree compose double
val comp1 = three.withUnit() compose double // HERE
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?
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? .
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
547
Appendix I: Chapter 9
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
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?
548
Appendix I: Chapter 9
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
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? .
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.
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.
fun main() {
"10" // 3
.lift()
.flatMap(::strToInt)
.map(::double)
.getOrDefault(-1)
.pipe(::println)
"10sa" // 4
.lift()
.flatMap(::strToInt)
.map(::double)
.getOrDefault(-1)
.pipe(::println)
}
549
Appendix I: Chapter 9
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
3. Use the same structure you used with Optional<T> with a valid String .
20
-1
Exercise 9.4
Implement a function that reverses a String using one of the folding functions
you’ve implemented in this chapter.
550
Appendix I: Chapter 9
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
4. Return the content of StringBuilder as a String .
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> ?
In this code:
551
Appendix I: Chapter 9
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
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 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
552
Appendix I: Chapter 9
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
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
553
Appendix I: Chapter 9
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
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
}
37
fun main() {
val list = List<Int>(37) { it }
list.average() pipe ::println
}
554
Appendix I: Chapter 9
Functional Programming in Kotlin by Tutorials Exercise & Challenge Solutions
You get:
18.0
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:
0
555
Functional Programming in Kotlin by Tutorials
J Appendix J: Chapter 10
Exercise Solutions
Written by Massimo Carli
Exercise 10.1
What’s the cardinality of the following type?
This is because:
The total number of possible values you can represent with Triplet is then
512.
Exercise 10.2
What’s the cardinality of the following type?
556
Appendix J: Chapter 10
Functional Programming in Kotlin by Tutorials Exercise Solutions
Exercise 10.3
What’s the cardinality of the following type?
They’re the same because addition is associative. You can find a function that
maps each value in MultiEither to each value in MultiEither2 and vice
versa. Because of this, the two types are isomorphic.
557
Functional Programming in Kotlin by Tutorials
K Appendix K: Chapter 12
Exercise Solutions
Written by Massimo Carli
Exercise 12.1
Can you find an example of a monoid whose operation isn’t commutative?
A set of String s
Concatenation
a + (b + c) = (a + b) + c
a + b != b + a
fun main() {
val str1 = "Hello"
val str2 = " World!"
println(str1 + str2)
println(str2 + str1)
}
Hello World!
World!Hello
What about the unit element? Of course, this is the empty String . To test this,
just add and run the following code:
558
Appendix K: Chapter 12
Functional Programming in Kotlin by Tutorials Exercise Solutions
fun main() {
// ...
val unit = ""
println(str1 + unit)
println(unit + str1)
}
Getting as output:
Hello
Hello
Exercise 12.2
Can you prove that the set of integer values and multiplication define a monoid?
In this case, what would the unit element be?
Multiplication is associative.
There’s a unit element.
a * (b * c) = (a * b) * c
a * 1 = a
1 * a = a
You can get a better idea of this with some simple code:
fun main() {
val a = 3
val b = 7
val c = 13
val res1 = a * (b * c)
val res2 = (a * b) * c
println(res1)
println(res2)
559
Appendix K: Chapter 12
Functional Programming in Kotlin by Tutorials Exercise Solutions
val unit = 1
val res3 = a * unit
val res4 = unit * a
println(res3)
println(res4)
}
273
273
3
3
Exercise 12.3
How would you implement the monoid MonoidIntMult for Int and
multiplication?
Exercise 12.4
How would you implement the monoid MonoidStringConcat for String and
String concatenation?
Exercise 12.5
In the chapter, you proved that addition is different from multiplication using
op(op(a, 1), 1) and op(a, 2) . The two expressions are equal for any Int
a if op is addition, but the same isn’t true if op is multiplication. Can you
implement a Property<Int> implementation for this rule and use it to create a
new test?
561
Appendix K: Chapter 12
Functional Programming in Kotlin by Tutorials Exercise Solutions
1. Define DoubleIncrementProperty as a Property<Int> implementation.
4. Invoke fn , passing 1 as the second parameter and then using the result to
invoke fn again.
class PropertyTestTest {
@Test
fun `Exercise 5 solution`() {
100.times {
val additionProp =
CommutativeProperty<Int>() and
DoubleIncrementProperty() and
IdentityProperty(0)
val evaluation = additionProp(IntGenerator) {
sum(it[0], it[1])
}
Truth.assertThat(evaluation).isTrue()
}
}
}
As you see, this is very similar to the test you implemented in the chapter using
DoubleIncrementProperty in place of AssociativeProperty .
562
Functional Programming in Kotlin by Tutorials
L Appendix L: Chapter 13
Exercise Solutions
Written by Massimo Carli
Exercise 13.1
How would you make the Optional<T> data type you created in Chapter 9,
“Data Types”, a monad?
@JvmStatic
fun <T> empty(): Optional<T> = None
}
}
In the chapter, you learned that you can implement the fish operator starting
from the implementation of the optionalFlatten function, which is a function
of type (Optional<Optional<T>>) -> Optional<T> . After that, you just need to
implement optionalBind and then optionalFish in the same way you saw in
the chapter.
563
Appendix L: Chapter 13
Functional Programming in Kotlin by Tutorials Exercise Solutions
fun <T> Optional<Optional<T>>.optionalFlatten(): Optional<T> = when
(this) {
is Some<Optional<T>> -> when (this.value) {
is Some<T> -> Optional.lift<T>(this.value.value)
is None -> Optional.empty()
}
is None -> Optional.empty()
}
Here, you just return Some<T> if both the Optional s are Some .
Exercise 13.2
What’s the relation between the fish operator, >=> , and flatMap ? Can you
express the latter in terms of the former for Optional<T> ?
((A) -> Optional<B>, (B) -> Optional<C>) -> (A) -> Optional<C>
The flatMap has type (Optional<A>, (A) -> Optional<B>) -> Optional<B> .
564
Appendix L: Chapter 13
Functional Programming in Kotlin by Tutorials Exercise Solutions
565
Functional Programming in Kotlin by Tutorials
M Appendix M: Chapter 14
Exercise Solutions
Written by Massimo Carli
Exercise 14.1
ResultAp<E, T> is very similar to Either<E, T> . Can you implement
flatMap for it as well?
As a quick note, notice that this flatMap implementation uses the fail fast
approach.
Exercise 14.2
In the previous paragraphs, you implemented a simple system to fetch and
parse data using both Optional<T> and Either<E, T> . Can you do the same
using ResultAp<E, T> ?
566
Appendix M: Chapter 14
Functional Programming in Kotlin by Tutorials Exercise Solutions
fun fetchTvShowResultAp(
query: String
): ResultAp<IOException, String> = try {
ResultAp.success(TvShowFetcher.fetch(query))
} catch (ioe: IOException) {
ResultAp.error(ioe)
}
Here, you just do the same replacements you did in Exercise 14.1.
Finally, you can run the following code to test your solution:
fun main() {
fetchAndParseTvShowResultAp("Big Bang Theory")
.errorMap {
println("Error: $it")
it
}
.successMap {
println("Result: $it")
}
}
Just note that Throwable as an upper bound forces you to return a Throwable
from errorMap . Again, feel free to remove that limitation if this bothers you.
567