## Functional Programming in Kotlin by Tutorials

First Edition · Android 12 · Kotlin 1.6 · IntelliJ IDEA 2022

#### Before You Begin

Section 0: 5 chapters

#### Section II: Data Types & Typeclasses

Section 2: 5 chapters

# 16. Handling Side Effects Written by Massimo Carli

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

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

In this chapter, you’ll learn:

• How to implement Hello World in a pure, functional way.

• What the `IO<T>` data type is.

• How to use `IO<T>` to compose functions with side effects.

• How to use `IO<T>` in a practical example.

• How to use suspendable functions to solve basically the same problem `IO<T>` wants to solve.

This is an essential chapter, and now it’s time to do some magic! :]

## From State<S, T> to IO<T>

Hello World is probably the most popular application to implement when learning a new language. This is mainly because it’s very simple and allows you to see how to execute some of the fundamental tasks in common between all applications, like compilation, execution, debugging and so on.

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
}
``````

In this code, you:

1. Print a message asking the user their name.
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?

You can follow this idea starting with the definition of a type you call `World`. Add the following in the World.kt file:

``````typealias World = Unit
``````

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:

``````typealias SideEffect = (World) -> World
``````

This is interesting because you’re defining a `SideEffect` as any function from an initial state of the `World` to, probably, a different state of the same `World`. But here, something strange is happening. If you have a function of type `SideEffect` able to capture the whole `World` in input and return a different version of it, you’ve essentially eliminated the concept of a side effect because everything happens in the context of that function. In this case, all the functions would be pure.

To prove that you can modify the initial program as the composition of the function, you use:

• `readName`, which reads the name from the standard input.
• `printString`, which prints a `String` to the standard output.

`readName`’s type is `(World) -> Pair<String, World>` because it receives the `World` in input and provides the `String` for the name and a new version of the `World` in output. Add the following code to Greetings.kt:

``````val readName: (World) -> Pair<String, World> = { w: World ->
Scanner(System.`in`).nextLine() to World
}
``````

`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:

``````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() {
// ...
printString("Hello Max \n")(World) pipe ::println  // 2
}
``````

In this code, you invoke:

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:

In the image, you can see:

1. An example of a `String` input.
2. The output of `readName`, which is a `Pair<String, Unit>` of the `String` in input and the new state of the `World` you previously defined using `Unit`.
3. The output you get using `print` in `printString`.
4. The output of `printString`, which is again a `Unit` representing the new state of the `World`.

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.

``````fun askNameAndPrintGreetings(): (World) -> World = // 1
{ w0: World -> // 2
val w1 = printString("What's your name? ")(w0) // 3
val (name, w2) = readName(w1) // 4
printString("Hello \$name! \n")(w2) // 5
}
``````
``````fun main() {
}
``````

### Hiding the world

In Chapter 15, “Managing State”, you implemented the `State<S, T>` monad as a data type encapsulating a `StateTransformer<S, T>` you defined like this:

``````typealias StateTransformer<S, T> = (S) -> Pair<T, S>
``````
``````typealias WorldT<T> = (World) -> Pair<T, World>
``````
``````val readNameT: WorldT<String> = readName
``````
``````val printStringT: (String) -> WorldT<Unit> = { str: String ->
{ w: World ->
Unit to printString(str)(w)
}
}
``````
``````infix fun <A, B> WorldT<A>.myOp( // 1
fn: (A) -> WorldT<B> // 2
): WorldT<B> = TODO() // 3
``````
``````   WorldT<A> // 1
-> (A) -> WorldT<B> // 2
-> WorldT<B> // 3
``````
``````   (World) -> Pair<A, World> // 1
-> (A) -> (World) -> Pair<B, World> // 2
-> (World) -> Pair<B, World> // 3
``````
``````   (World) -> Pair<A, World> // 1
-> (Pair<A, World>) -> Pair<B, World> // 2
-> (World) -> Pair<B, World> // 3
``````
``````infix fun <A, B> WorldT<A>.myOp(
fn: (A) -> WorldT<B>
): WorldT<B> = this compose fn.uncurryP()
``````
``````fun <T1, T2, R> ((T1) -> (T2) -> R).uncurryP():
Fun<Pair<T1, T2>, R> = { p: Pair<T1, T2> ->
this(p.first)(p.second)
}
``````

### A hidden greeting

The first implementation of `askNameAndPrintGreetings` you created forced you to carry the world on at each step.

``````fun askNameAndPrintGreetings(): (World) -> World =
{ w0: World ->
val w1 = printString("What's your name? ")(w0)
printString("Hello \$name! \n")(w2)
}
``````
``````fun askNameAndPrintGreetingsT(): WorldT<Unit> = // 1
printStringT("What's your name? ") myOp { _ -> // 2
readNameT myOp { name -> // 3
printStringT("Hello \$name! \n") // 4
}
}
``````
``````fun main() {
}
``````

So far, you’ve worked with `WorldT<T>`, which is an abstraction representing a `World` transformation. This `World` transformation is basically a side effect. It’s not so different from `StateTransformer<S, T>` when you replace `S` with the type `World`.

### The IO<T> data type

In the lib sub-package in this chapter’s material, you find all the files related to the `State<S, T>` monad. In State.kt, you find the following definition:

``````data class State<S, T>(
val st: StateTransformer<S, T>
)
``````
``````data class IO<T>(val wt: WorldT<T>)
``````

### 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:

``````data class IO<T>(val wt: WorldT<T>) {

companion object { // 1
@JvmStatic
fun <S, T> lift(
value: T // 2
): IO<T> = // 3
IO { w -> value to w } // 4
}
}
``````
``````operator fun <T> IO<T>.invoke(w: World) = wt(w)
``````

### 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:

``````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
}
``````

### IO<T> as an applicative functor

Applicative functors are useful when you want to apply functions with multiple parameters. In the same IO.kt, add the following code:

``````fun <T, R> IO<T>.ap(
fn: IO<(T) -> R>
): IO<R> =
IO { w0: World ->
val (t, w1) = this(w0)
val (fnValue, w2) = fn(w1)
fnValue(t) to w2
}
``````
``````infix fun <A, B> IO<(A) -> B>.appl(a: IO<A>) = a.ap(this)
``````

Finally, you want to give `IO<T>` the superpower of a monad, adding the implementation of `flatMap` like this to IO.kt:

``````fun <A, B> IO<A>.flatMap(
fn: (A) -> IO<B>
): IO<B> =
IO { w0: World ->
val (a, w1) = this(w0)
fn(a)(w1)
}
``````
``````infix fun <A, B> WorldT<A>.myOp(
fn: (A) -> WorldT<B>
): WorldT<B> = this compose fn.uncurryP()
``````

In the previous sections, you implemented `askNameAndPrintGreetingsT` like this:

``````fun askNameAndPrintGreetingsT(): WorldT<Unit> =
printStringT("What's your name? ") myOp { _ ->
printStringT("Hello \$name! \n")
}
}
``````
``````val readName: (World) -> Pair<String, World> = { w: World ->
Scanner(System.`in`).nextLine() to World
}

val printStringT: (String) -> WorldT<Unit> = { str: String ->
{ w: World ->
Unit to printString(str)(w)
}
}
``````
``````val readNameM: IO<String> = IO(readNameT) // 1

val printStringM: (String) -> IO<Unit> =
printStringT compose ::IO // 2
``````
``````fun <T> IO<T>.bind(): T = this(World).first
``````
``````fun askNameAndPrintGreetingsIO() : () -> Unit = { // 1
printStringM("What's your name? ").bind() // 2
val name = readNameM.bind() // 3
printStringM("Hello \$name! \n").bind() // 4
}
``````
``````fun main() {
}
``````

## The meaning of IO<T>

The greeting example you’ve implemented so far is a great example of a practical use of `IO<T>`. However, in Chapter 14, “Error Handling With Functional Programming”, you learned that sometimes things go wrong. For instance, you implemented `readNameM` like:

``````val readNameM: IO<String> = IO(readNameT)
``````
``````val readNameT: WorldT<String> = readName

val readName: (World) -> Pair<String, World> = { w: World ->
Scanner(System.`in`).nextLine() to World
}
``````
``````val safeReadName: (World) -> Pair<Result<String>, World> =
{ w: World -> // 1
try {
Result.success(Scanner(System.`in`).nextLine()) to World
} catch (rte: RuntimeException) {
Result.failure<String>(rte) to World
}
}

val safeReadNameError: (World) -> Pair<Result<String>, World> =
{ w: World -> // 2
Result.failure<String>(
RuntimeException("Something went wrong!")
) to World
}

``````
``````val safePrintStringT: (String) -> WorldT<Result<Unit>> =
{ str: String ->
{ w: World ->
Result.success(Unit) to printString(str)(w)
}
}
``````
``````val safeReadNameM: IO<Result<String>> = IO(safeReadNameT) // 1

val safePrintStringM: (String) -> IO<Result<Unit>> =
safePrintStringT compose ::IO // 2
``````
``````fun safeAskNameAndPrintGreetingsIO(): () -> Result<Unit> = { // 1
safePrintStringM("What's your name? ").bind() // 2
.flatMap { _ -> safeReadNameM.bind() } // 3
.flatMap { name ->
safePrintStringM("Hello \$name!\n").bind() // 4
}
}
``````
``````fun main() {
onSuccess = { _ ->
// All good
},
onFailure = { ex ->
println("Error: \$ex")
}
)
}
``````

``````val safeReadNameT: WorldT<Result<String>> = safeReadNameError
``````

``````suspend fun readStringCo(): String = // 1
Scanner(System.`in`).nextLine()

suspend fun printStringCo(str: String) = // 2
print(str)

@DelicateCoroutinesApi
fun main() {
runBlocking { // 3
printStringCo("What's your name? ") // 4
val name = async { readStringCo() }.await() // 5
printStringCo("Hello \$name!\n") // 6
}
}
``````

## 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.
• A transformation of the world is another way to define a side effect.
• Functions with IO operations are impure by definition.
• 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.
• You can easily give `IO<T>` the superpowers of a functor, applicative functor and monad.
• The `IO<T>` data type is a way to decouple a side effect from its description.
• `IO<T>` contains the description of a side effect but doesn’t immediately execute it.
• In Kotlin, a suspendable function allows you to achieve the same result as `IO<T>` in a more idiomatic and simple way.

## Where to go from here?

Congratulations! With this chapter, you took another crucial step in the study of the main concepts of functional programming with Kotlin. State management with the `IO<T>` monad is one of the most challenging topics forcing you to think functionally. In the last part of the chapter, you saw how the `IO<T>` monad can be easily replaced with the use of coroutines. In the following chapter, you’ll see even more about this topic and implement some more magic! :]

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.