loading...

Discussion on: Explain Monads Like I'm five

Collapse
bobbypriambodo profile image
Bobby Priambodo

Wouldn't programming be easy if we only deal with "simple" values?

You know: integers, strings, booleans... writing a function that takes a string and gives an int, things like that.

Unfortunately, most of the time, the values (or the computations of them) are not so simple. They may have "quirks", such as:

  1. The value may or may not exist!
  2. The value may exists, but there are more than one of them :(
  3. Getting the value would mean involving some kind of I/O operations...
  4. The value may exists... eventually in the future.
  5. The value may produce errors :(
  6. The value may depend on some kind of a state of the outside world.
  7. etc.

This is not an exhaustive list, but I hope you can see the pattern. Those quirks are all nitty gritty details... what if we don't have to deal with them? What if we could write functions that acts as if they don't exist? Surely as a software developer we can make some abstractions to solve it?

Well, monads to the rescue!

A monad acts as a container that abstracts away those quirks in the computations, and let us focus more on what we want to do with the contained values.

Let's get back on that list, shall we?

  1. The value may or may not exist, handled by the Maybe monad.
  2. The value may exists, but there are more than one of them, handled by the List monad (yes, List is a monad!).
  3. Getting the value would mean involving some kind of I/O operations, handled by the IO monad.
  4. The value may exists eventually in the future, handled by the Promise/Future monad (that Promise you use in JavaScript? It's a monad!--kind of).
  5. The value may produce errors, handled by the Error/Result/Either monad.
  6. The value may depend on some kind of a state of the outside world, handled by the State monad.

Amazing, isn't it? What's that? "If they're just containers, what's so special about them," you say?

Well, other than it being a container, it also defines a set of operations to work on that container. For this, let's introduce a term monadic value to refer to a simple value that is wrapped in a container. Those operations include:

  1. return: how to wrap a "simple" value into a monadic value? You return it!

  2. fmap: you have a function that takes a String and produces an Int. Can you use it for Maybe String to produce Maybe Int? Spoiler: yes, you fmap it!

  3. join: oh no, I have a Maybe (Maybe String)! How can I flatten it? Use join, and it will give you Maybe String.

  4. bind or chain or >>= (yes, that symbol, we have a name for it!): a combination of fmap + join, since that pattern (also called flatMap) occurs quite often.

  5. liftM, liftM2, liftM3, etc.: if we have a function that takes a String and produces an Int, can we construct a function that takes a Future String and produces a Future Int? Yes, you "lift" them to the monadic world!

  6. >=> (another weird symbol, I call it monadic compose): suppose you have a function that takes a String and outputs IO Int, and another function that takes an Int and produces IO Boolean, can you construct a function combining the two that takes a String and produces a IO Boolean? You guessed it, you compose them with >=>!

So that's it about monads! I hope it gives the 5-year-old you a practical understanding of how powerful this abstraction really is.

If you want more example, I've written a blog post about this here (this comment is actually a gist of the article).

Note: Before all the more initiated FP devs burn me, I realize that I'm conflating many things from Functors, Applicatives, and whatnots in this explanation, but I hope you forgive my omission, since omitting details might be necessary for 5-year-old-explanations. I also encourage the reader to study the theoretical foundation of it should you be interested :D

Collapse
rubyandcoffee profile image
Alexandra Wolfe

Absolutely incredible explanation - thank you!