IO: to be a Monad or not to be, that's the question!
Tags: functional, kotlin, io
Published: 2020-08-06 13:55:30

---
title: IO: to be a Monad or not to be, that's the question!
published: true
description: a brief tour of monads and an example using IO
tags: #fp, #kotlin, #io
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/5zuf5am9fp08128ztjhe.jpeg
---

Hello folks, 
I promised you in [How to live in a pure world](https://dev.to/eureka84/how-to-live-in-a-pure-world-5fbj) I would have written a full article showing an example of how to deal with IO functionally and here it is!

I will use Kotlin and its functional companion library: Arrow. 

# A brief history of Arrow

Arrow was born as a merge of two functional libraries in the Kotlin universe: __FunKtionale__ and __Kategory__. The contributors of the two libraries decided to join forces and so __Arrow__ was born.

Like other libraries in other programming languages (like ScalaZ and Cats in Scala), Arrow tried to provide the same concepts of typed functional programming that were born in the Haskell universe: so they started out defining some core data types (Option, Try, Either, Validated, etc.) and a bunch of [typeclasses](http://learnyouahaskell.com/types-and-typeclasses) like Functor, Semigroup, Monoid, Monad, and the likes (much more to be true).

The current version of Arrow is 0.11.0-SNAPSHOT. Arrow 0.11.0 should be released by September 2020, and the first major release should come out by the end of the year, even if it will not be marked as 1.0 but as 1.4, following the Kotlin compiler version (because of Arrow Meta).

What the team is working on to target the first stable release is to simplify and stabilize the api. 

For example, they have already deprecated and they are going to remove the `Try<A>` data type, because it favors an eager evaluation of code that can have side effects, in favor of `Either<E, A>`. If you think about it you could say that:

```kotlin
typealias Try<A> = Either<Throwable, A> 
```
and the non-eager evaluation is granted by the fact that, aside for right/left value constructors, the function `Either.catch()` you use to wrap throwing blocks accepts only a suspended block (more on this later).

The same kind of simplification occurred also for `Option<A>`. There were some questions in the Kotlin community towards the actual need for such a data type, given the Kotlin language features nullable types. So Arrow maintainers have decided to ditch the `Option` data type in favor of nullable types or, once again, `Either`s, providing also easy means to go from nullable types to `Either`s. Example:

```kotlin
val a: A? = null 
val b: Either<Unit, A> = Either.fromNullable(a)
// and if you like you can define  
typealias Option<A> = Either<Unit, A> 
```

# Monads
Before addressing the title question about whether or not IO should be modeled as a Monad, let's talk a bit about monads.

What is a Monad?

Last time I haven't spent much time on the subject because I wanted to focus more on the reason why IO operations were intrinsically impure 

So, without further ado, here is maybe the most concise definition you could find about what a Monad is: 

> A **monad** is a __monoid__ in the __category__ of __endofunctors__.

The problem is this definition requires you to know what is a __monoid__, a __category__, and an __endofunctor__. 

I'll try to provide you a short definition of all three.

## Monoid
A **monoid** is a __semigroup__ with a unit element, that respects the monoid laws. A semigroup is a set A with an internal operation, usually called `combine`, that is __associative__. 

```kotlin
interface Semigroup<A> {
	fun combine(a: A, b: A): A
}

interface Monoid<A>: Semigroup<A> {
	val empty: A
}
```

The monoids laws are
1. associativity (because it's a semigroup): for all a,b,c in A it must be true that `combine(a,combine(b,c)) = combine(combine(a,b),c)`
2. left identity: for all a in A `combine(empty,a) = a`
3. right identity: for all a in A `combine(a,empty) = a`

## Category 
This is a bit more abstract. A category C is a pair (O, M) where 
- O is a collection of objects (we are not interested in better defining them)
- M is a collection of morphisms that connects an object A in O to an object B (m: A -> B)

For C to be a category it must obey to the category laws:
1. There must exist for every A in O an identity morphism 1<sub>A</sub>: A -> A
2. There must exist an operation, called composition, indicated by `.` such that for every 2 morphisms in M, f: A -> B and g: B -> C, it exists a morphism g `.` f: A -> C. This operation must have 2 properties
	1. associativity: for every 3 morphism in M, f: A -> B, g: B -> C and h: C -> D then it must be true that (f `.` g) `.` h = f `.` (g `.` h)
	2. identity: for every f: A -> B then 1<sub>A</sub> `.` f = f `.` 1<sub>B</sub>

Why are categories relevant for programmers? Well, because you can say that a programming language is a category where
- O is the collection of its types
- M are the functions
- The composition of morphism is the composition of functions. 

## Functor
What is a functor? It's a map between categories that preserves the category structure. To be more precise, given 2 categories C and D, F is a functor if 
- for every object X in C, it maps an F(X) in D
- for every morphism f: A -> B in C it maps the morphism F(f) = F(A) -> F(B) in D
- for every X in C it maps the identity morphism in C into the identity morphism in D, i.e. F(1<sub>x<sub>C</sub></sub>) = 1<sub>F(X)<sub>D</sub></sub>
- for every two morphism in C, f and g, F(f `.` g) = F(f) `.` F(g)

An endofunctor is just a functor from a category C into C itself. 

Given the previous parallelism drawn between a category and a programming language, you might be wondering: what is an endofunctor in a programming language? 
Well, if you think about it, it should be something that maps its types (the objects) but also its functions (the morphisms) between the basic types (from the original category) into functions between the __mapped__ types (in the __new__ category, the one where all the objects have the form `F<X>`). 

So an endofunctor F in Kc (the Kotlin types category) is a pair F = (F(X), lift) where 
- F(X) is a type constructor (i.e. F(X) is a new type in Kc) 
- lift is a function 
	```kotlin
		fun <A,B> lift(f:(A) -> B): F<A> -> F<B>
	```

I guess all this might seem a bit abstract, so an example might help. 

Ex.: `Option<A>` is a functor. 

Why? Because it is a type constructor (from A to `Option<A>`) and it exist a lift function
```kotlin
	fun <A,B> lift(f:(A) -> B): Option<A> -> Option<B> = 
		{ a: Option<A> -> a.map(f) }
```

## Putting it all together
From all the definition above we can come back to a more operative definition of a monad: a monad is a type constructor with three functions

```kotlin
// pseudo code (to actually encode a generic monad one has to encode first the concept of higher kinded types)
interface Monad<A> {
	fun of(t: A): Monad<A> = // omitted
	fun <B> map(ma: Monad<A>, f: (A) -> B): Monad<B> = // omitted
	fun <B> flatMap(ma: Monad<A>, f: (A) -> Monad<B>): Monad<B> = // omitted
}
```

that obeys to the following laws 

- left identity: `flatMap(of(x), f) = f(x)` 
- right identity: `flatMap(x, of) = x`
- associativity: `flatMap(flatMap(ma, f), g) = flatMap(mx) {x -> flatMap(f(x), g) }`

> *Note*: the actual encoding in Arrow of Monads is a bit different, the above pseudo-code was made just to give a general idea. 

# IO monad

`IO` is a monad and in Arrow is undergoing the same simplification process shown above for the core data types: more specifically it's going to be deprecated and removed from the library (this time not in favor of `Either`). 

The reasons in this case are different. 

The first one is that Kotlin has a language feature (suspended functions and coroutines) that can better express the intent of a data type like `IO<A>`, with native support in the language. 

Secondly, if you ever try to write actual programs using `IO`, you will probably end up with something like `IO<Either<E, A>>`, that is not so ergonomic to work with. 

Let's see it with an example (finally, am I right?)

```kotlin
data class FileName(val path: String)
data class Employee(
  val lastName: String, 
  val firstName: String, 
  val birthDate: LocalDate, 
  val emailAddress: EmailAddress
)
data class EmailAddress(val value: String)

sealed class ProgramError
data class ReadFileError(val path: String): ProgramError()
data class ParseError(val source: String): ProgramError()
data class MailSendingError(
  val emailMessage: EmailMessage
): ProgramError()

typealias Program = 
	(FileName) -> IO<Either<ProgramError, Unit>>
typealias LoadEmployees = 
	(FileName) -> IO<Either<ProgramError, List<Employee>>>
typealias EmployeeFilter = (Employee) -> Boolean
typealias SendBirthdayGreetings = 
	(Employee) -> IO<Either<ProgramError, Unit>>

fun createSendGreetingsFunction(
    loadEmployees: LoadEmployees,
    employeeBornToday: EmployeeFilter,
    sendBirthDayGreetingMail: SendBirthdayGreetings
): Program = { sourceFile: FileName ->
    EitherT(loadEmployees(sourceFile)).flatMap { employees ->
        employees
            .filter(employeeBornToday)
            .map { e -> EitherT(sendBirthDayGreetingMail(e)) }
            .sequence().map { Unit }
    }.value().fix()
}
```

This is a snippet of code from my take on the birthday greetings kata by Matteo Vaccari (full code [here](https://github.com/eureka84/birthday-greetings-kata-kotlin.git), tag `io-either-version-arrow-0.10.5`).

The code per se is pretty straightforward: it will load a list of employees from a csv file, filter the employees born today and then send them a birthday greeting email. 

To execute the program description built using `IO`s one need to do something like the following at the edge of his/her world (the main function, your rest controller, etc.)

```kotlin
val program = sendGreetingsToAll(FileName("/fixtures/bigFile.csv"))
program.unsafeRunSync()
```

> **NOTE**: one common mistake one can make is to continuously create and run `IO`s. That is not the way (or it is not supposed to be). You should build your entire __description__ of what your program does and then only run it once at the end of your world (so to speak).

The following are the functions in the previous snippet of code that have a side effect and have been modeled with `IO`

```kotlin
typealias LoadEmployees = 
	(FileName) -> IO<Either<ProgramError, List<Employee>>>
typealias SendBirthdayGreetings = 
	(Employee) -> IO<Either<ProgramError, Unit>>
typealias Program = 
	(FileName) -> IO<Either<ProgramError, Unit>>
```

What is the issue with them? 
Well, it's something related to their composition. 
Given that their return type is an `Either` nested inside an `IO`, working with it becomes a little cumbersome because, even if they are both Monads (`IO` and `Either`), and monads should make it easier to sequence transformations, having two nested monads you should first `map` or `flatMap` on the external one, `IO`,  and then on the internal one, `Either`. 

One would have hoped to be able to write

```kotlin
val f: (A) -> IO<Either, B> = // omitted
val a: IO<Either<E, A>> = // whatever
val b: IO<Either<E, B>> = a.flatMap(f)
```

But it's not possible. You have to write something like this

```kotlin
val f: (A) -> IO<Either, B> = // omitted
val ioA: IO<Either<E, A>> = // whatever
val ioB: IO<Either<E, B>> = 
	ioA.flatMap { eitherA: Either<E, A> -> 
		eitherA.fold(
			{ e: E -> IO.just(e.left()) }, 
			{ theActualA: A -> f(theActualA) }	
		)
```

That's the reason why you have seen in my previous snippet that `EitherT`. What is it? It is a Monad Transformer and let you compose two nested monads in case the inner one is an `Either`; in lemon terms, it's a data type that wraps the `IO<Either<E, A>>` and let you regain the easiness of sequencing operations.

Let's see how that would apply in the last example

```kotlin
val f: (A) -> IO<Either, B> = // omitted
val ioA: IO<Either<E, A>> = // whatever
val ioB: IO<Either<E, B>> = 
	EitherT(ioA).flatMap { theActualA: A -> 
		EitherT(f(theActualA)) 
	}.value().fix()
```

Monad transformers solve a problem, i.e. the fact that two Monads cannot be generically composed to produce another monad; the question is: is it the right problem to solve? 
We have introduced `IO` to re-gain purity and referential transparency but it also adds already one wrapper layer around our impure logic; if, when we are using `IO` in real life, we end up having an `IO<Either<E, A>>`, and to work with it we have to use an `EitherT`, adding another extra layer with both a cognitive and performance overhead, one might ask: is it worth it?

# Suspended functions
The answer is: maybe no. In Arrow 0.11.0-SNAPSHOT the maintainers decided to ditch the IO monad in favour of suspended functions. What does it mean? Let's have a look at the `sendBirthdayGreetings` example revised 

```kotlin
typealias LoadEmployees = 
	suspend (FileName) -> Either<ProgramError, List<Employee>>
typealias EmployeeFilter = (Employee) -> Boolean
typealias SendBirthdayGreetings = 
	suspend (Employee) -> Either<ProgramError, Unit>
typealias SendGreetings = 
	suspend (FileName) -> Either<ProgramError, Unit>

fun createSendGreetingsFunction(
    loadEmployees: LoadEmployees,
    employeeBornToday: EmployeeFilter,
    sendBirthDayGreetingMail: SendBirthdayGreetings
): SendGreetings = { sourceFile: FileName ->
    loadEmployees(sourceFile).flatMap { employees ->
        employees
            .filter(employeeBornToday)
            .map { e -> sendBirthDayGreetingMail(e) }
            .sequence().map { Unit }
    }
}
```

Can you spot the difference? That's right here it is 

```kotlin
typealias LoadEmployees = 
	suspend (FileName) -> Either<ProgramError, List<Employee>>
typealias SendBirthdayGreetings = 
	suspend (Employee) -> Either<ProgramError, Unit>
typealias SendGreetings = 
	suspend (FileName) -> Either<ProgramError, Unit>
```

All the functions that returned an `IO<Either<ProgramError, A>>` in the previous version, now simply return an `Either<ProgramError, A>` but are marked as `suspend`, signaling they need a safe environment to be executed (arrow-fx-coroutines provides such an environment). 

So the `suspend` functions take the place of functions returning IO because they are, for all intent and purposes, __delayed__ and conveying the same concept as the `IO` monad: something impure is going on.

Here is how you could run the previous function at the end of your world (in my case in the acceptance test)

```kotlin
import arrow.fx.coroutines.Environment

Environment().unsafeRunSync {
    sendGreetingsToAll(FileName("/fixtures/bigFile.csv"))
}        
```

Easy, no?

# Conclusion

>IO: to be a Monad or not to be?

Arrow guys answer is: no, thank you. We have already a Kotlin construct that is more than enough to describe impure interactions with the external world: suspended functions. 

What else? I don't know, I only hope to have given you a feel of how to use IO, monad, or not, in a __real-life__ example.

------------------

# References

* [Arrow docs](https://arrow-kt.io/docs/0.10/core)
* Bartosz Milewski category theory for programmers: [videos](https://www.youtube.com/watch?v=I8LbkfSSR58&list=PLbgaMIhjbmEnaH_LTkxLI7FMa2HsnawM_), [book](https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/)
* Giulio Canti [functional programming notes](https://github.com/gcanti/functional-programming) (only for those of you who speak Italian and are versed in Typescript) 
* Scott Wlaschin visual explanation of the [__elevated world__](https://fsharpforfunandprofit.com/posts/elevated-world/)
* [A nice little post](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html) explaining functors, applicatives, monoids and monads in pictures