Angelo Sciarra

Posted on

# How to live in a pure world

Hello folks,
here I am with a follow-up of the previous article A matter of purity

In it I talked about what is a pure function and why should you even care.

I can imagine some questions may have arisen while reading it:

• how to make partial functions total?
• how to make pure and referentially transparent functions that perform some sort of side effect (like IO)?

Let's address these issues one at a time.

# Make partial functions total

Just to get everybody on the same page let me recall what a partial function is:

given `f: A -> B` f is partial if it is not defined for all a in A.

How can one work on `f` to get an `f'` such that `f'` is total?

Well, there are 2 paths one can take:

1. restrict f domain A to those elements where f is defined, i.e.

`f': A' -> B` where `A'` is made of all `a in A` such that it exists a `b in B` where `b = f(a)`

Thinking about `f` as a function we can write in Kotlin or Java this path seems a bit tortuous;

2. augment B with a synthetic value that maps all a in A where f is not defined

`f': A -> B'` where B' is `B union { epsilon }` and for all a in A where f is not defined then `epsilon = f'(a)`

This second approach is the one we can pursue in our code using data types like `Option` and `Either` because when we say that our function

``````val f: (A) -> Option<B>
``````

what we are saying is exactly that whenever we cannot return `Some(a)` we will return `None`. (Same goes for `Either`, whenever we cannot return `Right(a)` we will return `Left(error)`).

# Make side effects pure

Once we have made our functions total, have we made them pure?

Let's see an example

``````fun readFileLines(path: String): List<String> =
``````

We know that this function may throw an IOException so, from what we said above about making partial functions total, we could turn it into

``````fun readFileLines(path: String): Either<IOException, List<String>> =
try {
} catch (e: IOException) {
e.left()
}
``````

Now the function is total (you can argue that returning IOException as the error type may not be the best design choice, but we'll leave it like that for the time being).

But is it pure? Is it true that:

given the same `path` input it will always return the same `Either` value, be it a `Right` value or a `Left` one?

The answer is no because anytime we call this function we are interacting with the external world and we have no control or knowledge of what this world may look like at any time.

In the functional universe (well, to be precise, in the Haskell orbiting part) there is an abstraction meant exactly to describe this interaction. Let me introduce you the

Leaving aside the scary m-word, let's focus on the IO abstraction.

The idea behind it is simple: given that the interaction with the external world is not predictable, we are going to work with a type that is designed like:

``````data class IO<A>(run: () -> A) {

fun map<B>(f: (A) -> B): IO<B> = // omitted

fun flatMap<B>(f: (A) -> IO<B>): IO<B> = // omitted

[...]
}
``````

Can you see it? All that matters is that `() -> A`.

Why?

Because it is as if you are saying: provided that I promise to give you an A, you can transform it via `map`, if your transformation is a pure function, or via `flatMap`, if your transformation has again some kind of interaction with the external world.

We have transformed the real, unpredictable interaction with the external world with a description of it, simply introducing a delay. What we gained by doing so is that this description is once again a value, and we can play around with values freely.

NOTE: Kotlin has a construct built in the language, suspended functions, that can be thought a bit as our delay function (`() -> A`). For that reason, the creators and maintainers of Arrow (the functional companion for Kotlin) have decided to redesign their implementation of the IO data type to exploit suspended functions (and coroutines). I promise I will make an article dedicated to it, as soon as I manage to fully understand it myself :D

## Conclusion

Is it easy to live in a pure world?

That is not really for me to tell you, only you can judge if what you gain is worth the pain (if you want to call it so). Nonetheless, I think that expanding your horizons and knowing about what it means to live in a pure world will provide you with a new perspective on how to interact with the world that can be useful also if you choose to remain in an impure world.

I hope to have given you some explanations of concepts you may have heard of but never managed to wrap your head around.

Well folks, what else to add?

To infinity and beyond!

Pure IO through coroutines - sounds quite interesting. The main difference to a thunk like `() => A` seems to be the capability to send a value to the suspended function. Are Kotlin's coroutines multi-shot, i.e. can you resume them from a certain position more than once?