Skip to content

Instantly share code, notes, and snippets.

@BalmungSan
Created November 15, 2022 21:15
Show Gist options
  • Save BalmungSan/ed7943254fc7df5864ebfed10a449013 to your computer and use it in GitHub Desktop.
Save BalmungSan/ed7943254fc7df5864ebfed10a449013 to your computer and use it in GitHub Desktop.
What is a Monad and why you shouldn't care

What is a Monad and why you shouldn't care

People trying to learn about FP will heard about the M word sooner than latter. Personally I believe that way sooner than what is required, and recommended; which is what makes folks to be confused about it.

This is my humble attempt to try to briefly explain the concept, while also arguing that you shouldn't really care too much about it. This all with the intention to make your FP journey more enjoyable 😄 — without further ado, let's start.

Motivation behind Monads

IMHO, it is better to first see the problem that leads us to them.

One of the principles of FP is composition, the idea is that FP code should be able to compose. i.e. break big problems into smaller ones, solve those and then combine the individual solutions into a big program (note, actually all programming paradigms want this). The classical example of how FP achieves that (and the reason why it got the name) is that "pure" (I hate that word) functions, as defined in maths, compose out of the box. So if you have f: A => B & g: B => C and you want h: A => C all you need to do is h = f andThen g

However, as you may be aware, pure functions alone are not enough to solve real-life problems. This is the inflection point of FP, there are multiple ways to tackle this problem; including not bothering at all and saying FP is just a useless fad (well, if you can call that a solution) The one we will be looking at today is using "effect types" to reify the runtime bits that pure functions can't express back into values (this is why we like to call this paradigm "Programs as Values"); just for completeness, note there are other solutions like:

  • Functional core, imperative shell.
  • Actors.
  • Algebraic effects (e.g. Unison and Caprese).
  • Data-driven programming (popular in Clojure).
  • Others.

Now, on "Programs as Values" land, we tackle the problem using contexts. For example, the Option context is used to transform partial functions into total ones; e.g. division doesn't work when the divisor is 0, but we can fix that by returning None instead. However, the problem is that once we have a context F, and our functions become like this: f: A => F[B] & g: B => F[C] then getting h: A => F[C] is not just function composition, we need something else. The solution is what is called Monad; that is why I like to say Monad is just an interface.

Monad is defined like this:

trait Monad[F[_]] {
  def lift[A](a: A): F[A]
  def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
}

Given that interface, we can write h again as

val h: A => F[C] = a => f(a).flatMap(g)

As long as, we know that Monad[F] exists; i.e. that F forms a Monad

This also means, that, in essence, we are just delegating the problem to someone else. We recover the ability to write programs that use F, but now F must knows how to compose itself. In practice, however, this is not a problem since you rarely invent new contexts, rather you just use existing ones like IO

BTW, note that flatMap also implies sequencing, the F[B] can't happen until F[A] has happened; since we need the A produced by the previous step to decide which will be the next one. Which is why you hear people say flatMap is the ; of FP; a phrase that only makes sense once you understand what a Monad is.

This is also why understanding Monad is too hard, because is too simple to actually mean something. That is why I rather recommend understanding IO and its flatMap instead. Monad is just a way to abstract over multiple different but deeply similar structures.

Final note, the laws of flatMap imply that you can un-nest nested flatMap calls. This is also the other point why they are interesting, they allow to solve the callback hell problem. Which is why Promises in Javascript and Futures in Scala where created in the first place.

Why does (doesn't) it matter

Monad as a concept is very important at a fundamental level, since is one of the core ideas behind most IO implementations. However, that doesn't mean that you need to understand it in order to be a FP programmer; it is similar to having to understand Maxwell's equations in order to use a bulb.

While I do think learning how to write monadic code is important, I believe you can learn that by just focusing in IO rather than in Monad.

Actually, the only reason why Monad and friends exist at all as a typeclass in Scala / cats, is to be able to abstract over multiple data types and write very generic and reusable functions, like traverse But, you rarely need to do that; especially while learning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment