Monads, a bit of Functional flow into Object world.
Tags: functional, crystal, computerscience
Published: 2020-02-10 10:03:44

# What's a monad

Monads are a mathematical concept that comes from the functional programming world.
Like all mathematical functional programming principles, the name isn't self explicit and I prefer the term of Wrapper or DataBox that may be simpler for developers.

Imagine you have a function A that process data and could fail.
There are multiple ways to receive the error like global error variable, multiple results (like tuple), or exception.

A Global variable error will couple all functions that will rely on it. 
Exceptions could leak out of the program and blow up to the user.
Multiple results like in Go add a lot of `if` statements and it's painful to read.

Monads add another arrow to development flow handling by adding one more way of managing errors.
Think of this as a box, instead of returning the value directly, the function returns the monad box.
The data inside the box could be present or not, but you don't have to think about this for now, because you are manipulating a box, not the data.
This box can be manipulated by various functions like `fmap` or `bind`, but we will discuss this later.

# How To manipulate Monads?

For starters, we'll initialize our monad.

```crystal
monad = Monads::Just.new(55)
```

Then, we will do data manipulation to the box.
To manipulate the data inside the monad, we use the `fmap` function that
will modify the value only if the box is in a correct state.

```crystal
monad = Monads::Just.new(55)
 .fmap { |data| data.to_s }
 .fmap { |data| data + additionnal_data }
```

As you can see, we are transforming our data from integer to string, then add some additionnal_data.
But what's the content of this variable?

```crystal
random_number = rand(0..2)
additionnal_data = random_number != 2 ? "00" : "abc"
```

additionnal_data is a string that can be `00` or `abc`, great.

At this point, our monad can be

```crystal
Monads::Just(String){"5500"}
Monads::Just(String){"55abc"}
```

Everything's fine, our data is always correct, why bother with a monad?
Because if something could go wrong, it will be.

Let's transform our monad value into an integer.

```crystal
monad = Monads::Just.new(55)
 .fmap { |data| data.to_s }
 .fmap { |data| data + additionnal_data }
 .bind { |data| Monads::Try(Int32).new(-> { data.to_i }).to_maybe }
```

Here, two possible cases.
Data is a correct integer representation and transforms without any troubles.
Or data is an incorrect integer, and something can blow up.
As data.to_i is not designed to use monads, we wrap it into a `Monads::Try`.
This helps us to transform non-monad-exception-handling to a correct monad.
If an exception occurs, `to_maybe` will transform the result into a `Monads::Nothing` object that will turn the monad into a failure state.

Note the usage of `bind` instead of `fmap`.
Here we return a monad, not a value, so the whole monad should be changed.

At this point, our monad can be
```crystal
Monads::Just(Int32){5500}
Monads::Nothing(Int32)
```

Now, what happens if I want to modify the value while it is invalid?

Nothing, nothing happens and Nothing is returned. (Literally)

Let's multiply the monad by two.

```crystal
monad = Monads::Just.new(55)
 .fmap { |data| data.to_s }
 .fmap { |data| data + additionnal_data }
 .bind { |data| Monads::Try(Int32).new(-> { data.to_i }).to_maybe }
 .fmap { |data| data * 2 }
```

At this point, our monad can be
```crystal
Monads::Just(Int32){11000}
Monads::Nothing(Int32)
```

# Other advantages of monads

Monads are great to control IO data, like fetching database or API calls.

Let's simulate a database request like this :

```crystal
record User, name : String

def find_data_from_database
 sleep 1
 puts "SELECT * FROM users;"
 sleep 1
 [User.new("John"), User.new("Anna")]
end
```
Our code needs users but not for instance.
It demands to do something expensive before needing users, but it will require them anyway.

```crystal
monad = Monads::Task(Array(User)).new(-> { find_data_from_database })
sleep 3 # Do expensive action
puts "After sleep"
result = monad.to_maybe
pp result
```

The console output will be 

```crystal
SELECT * FROM users;
After sleep
Monads::Just(Array(User)){[User(@name="John"), User(@name="Anna")]}
```
As you can see, the query was finished before the "After sleep" appears.

# Where should I sign to use it?

You can use my shards for crystal at https://github.com/alex-lairan/monads
Or just search a library that implements monads for your language :)