DEV Community

Discussion on: Explain Monads Like I'm five

Collapse
 
kayis profile image
K • Edited

Monads are packaging, that can re-package itself.

First lets talk about functors, since monads are a special kind of functor.

Think about chocolate, you want to eat it but you also want to take it somewhere without it getting dirty.

You have to unwrap it to eat a piece but put the wrap back around it when you take it to your friends.

Or eggs, you want them in a carton so you can put them in bunches in your fridge, without rolling around, but you have to get those you want to eat out, before eating them.

Put your values in a list or array to store them in groups.

Put your values in a promise or task, so you can move them through your software before you calculated them.

put your values in a maybe or option, if you're not sure if it's okay to use it somewhere.

The nice thing about these concrete functors I listed here is, they can all be implemented with the same interface. Lets get them all a map method and you don't have to care anymore.

functor.map(oneValue => ...) works for all of these.

Arrays and lists will call your callback for every value stored.

Promises and tasks will call your callback when the value they calculating some time in the future is ready.

Maybes and options will call your callback right in that instant if a value is in them and if not, they won't ever call it so you don't have a special null case anymore.

The thing that makes functors to monads is a method, often called flatMap, that lets you return a value in another monad of the same type and doesn't nest them, but flattens them out.

Imagine, you exchange every egg of your carton for another carton filled with a few eggs, you wouldn't be able to store every new carton inside your old carton, but maybe you would be able to store every egg of your new cartons inside the old carton. So you would have to open up every new carton, get out every egg and put it in the old one. Monads do this for you with a method called flatMap.

[1,2].map(oneValue => [oneValue * 1, oneValue * -1]) would give you [[1,-1],[2,-2]], but maybe you want [1,-1,2,-2] so you use [1,2].flatMap(oneValue => [oneValue * 1, oneValue -1])

Same goes for the other monads.

A promise or task that results in creating another promise or task inside the map? Lets use flatMap instead so you can chain the whole thing.

getDataAsync().flatMap(data => parseDataAsync(data)).map(data => log(data))

maybeImNotUseful.flatMap(value => maybeIcreateSomethingNew(value)).map(value => ...)

This would look rather ugly without flatMap