What is this? Amateur Hour? Flatmap that shit….

Saurabh Kishore Tiwari
The Thought Mill
4 min readMay 7, 2024

--

credits — http://www.flatmapthatshit.com/

Two guys were peer programming and one of the guys started writing this…

option match {
case Some(x) =>
case None =>
}

That’s when a very famous idiom was used — WHAT IS THIS? AMATEUR HOUR? FLATMAP THAT SHIT !!

What’s with the flatmap, you wonder…

Well, in the land of Scala, options are all about monads. And if you’ve ever taken even a slight interest in functional programming, you might have heard of monads and their trusty sidekick, flatmap. If you’ve not, let me put it simply for you. It’s like Batman and Robin, but for code.

What are monads, you ask?

Here’s the definition from wikipedia

In functional programming, a monad is a structure that combines program fragments (functions) and wraps their return values in a type with additional computation

Sounds confusing? Before breaking it down, let’s play with your brain some more…

For a monad m, a value of type m a represents having access to a value of type a within the context of the monad — C. A. McCann

In easier terms, monad is a wrapper around a value. But again.. why do we need a wrapper?

Well, think back to those endless hours you spent poring over design patterns. If your answer to why you did that was just to ace some interview, then buddy, you missed the point. Design patterns aren’t just about impressing interviewers — they’re like the blueprint of the coding world.

Back in the day, when humans started the art of building, they noticed certain patterns kept popping up: walls, doors, windows, you name it. And surprise, surprise the same thing happens in software! We’ve got our own set of recurring patterns: functions, variables, types, the whole shebang.

And that’s where monads come in. They’re just another design pattern, like the cool kid on the block. But here’s the kicker: unlike those flashy singletons, monads can sneak up on you. You might be using them without even realizing it, like stumbling upon something and incorporating it, without even realizing what a treasure it is.

So, next time someone says “flatmap,” just remember that it’s not just a cool-sounding word. It’s a huge but hidden part of a whole world of design patterns, lurking in the shadows of your code.

Now, out of respect, we’ll talk a little maths here, because monads come from Category theory in mathematics, also known as “abstract nonsense”.

Monads as Category Theory

Wikipedia —

In category theory, a monad (also triple, triad, standard construction, and fundamental construction) is a monoid in the category of endofunctors of some fixed category. An endofunctor is a functor mapping a category to itself, and a monad is an endofunctor together with two natural transformations required to fulfill certain coherence conditions.

What are these two natural transformations?

  1. Unit aka the “eta” or “return” function: This natural transformation lifts a value into the monadic context. It takes a plain value and wraps it inside the monad. In Scala, this is also referred to as apply().
  2. Multiplication aka the “mu” or “join” function: This natural transformation takes nested monadic values and collapses them into a single monadic value. It’s like flattening out the layers of the monad to reveal its inner value. In Scala, it’s commonly represented as flatMap.

But just following the above two transformations does not guarantee a monad. It must follow these three laws:

Left Identity
The left identity law states that applying the unit function to a value and then using the monadic bind operation should be the same as just applying the function directly to the value.

def double(x: Int): Option[Int] = Some(x * 2)
val a = 5
val leftIdentity = Some(a).flatMap(double) == double(a)

Right Identity
The right identity law states that composing the unit function with the monadic bind operation should be the same as just applying the function itself.

val m = Some(7)
val rightIdentity = m.flatMap(Some(_)) == m

Associativity
The associativity law ensures that the order of operations doesn’t matter when chaining monadic computations.

def double(x: Int): Option[Int] = Some(x * 2)
val m2 = Some(10)
val associativity = (m.flatMap(double).flatMap(double)) == (m.flatMap(x => double(x).flatMap(double)))

Monads are extensively used in Scala. Some examples are:

  1. Option
  2. Either
  3. Try
  4. List
  5. Future

Here’s how I tried implementing monad,

object MonadsBeginner extends App {
val userMonad = UserDefinedMonad("My name is Saurabh.")
val flatMapOnUserMonad = userMonad
.flatMap(x => UserDefinedMonad(x.concat(" What is your name?")))
.flatMap(y => UserDefinedMonad(y.concat(" Where are you from?")))

println(flatMapOnUserMonad)

}

//Apply method to wrap a variable into the monad
object UserDefinedMonad{
def apply[A](x: A): UserDefinedMonad[A] = if(x == null) NoneMonad else SomeMonad(x)
}

sealed abstract class UserDefinedMonad[+A]{
def flatMap[B](f: A => UserDefinedMonad[B]): UserDefinedMonad[B] = f(this.get)

def get: A
}

// Possible state of the monad
case class SomeMonad[A](value: A) extends UserDefinedMonad[A]{
override def get: A = value
}

// Possible state of the monad
case object NoneMonad extends UserDefinedMonad[Nothing] {
override def get: Nothing = throw new NoSuchElementException("NoneMonad.get does not exist")
}

Output

SomeMonad(My name is Saurabh. What is your name? Where are you from?)

--

--