We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies.

We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies. Less

We use cookies and other tracking technologies... More

Login or register
to apply for this job!

Login or register to start contributing with an article!

Login or register
to see more jobs from this company!

Login or register
to boost this post!

Show some love to the author of this blog by giving their post some rocket fuel 🚀.

Login or register to search for your ideal job!

Login or register to start working on this issue!

Engineers who find a new job through Functional Works average a 15% increase in salary 🚀

Blog hero image

Understanding Functor and Monad With a Bag of Peanuts

Sujit 23 April, 2018 | 7 min read

When I started coding in Scala I heard someone saying List, Option, Future etc. are all monads and functors. I wanted to understand what exactly is a monad or a functor.

A quick google search and I got articles explaining it like this

1_H_oTOUCEVJyj6AtLRamZxg.jpeg

Seriously?

1_azuj4rrMaIRgfswUzsPI1Q.png

Once upon a time, there was a supermarket named Scala. I went there to buy 1kg of peanuts.

I bought 1KG of peanuts but I was looking for something to carry them as obviously I could not take them home without a bag.

“May I help you?” a gentlemen asked me.

“I want to wrap these peanuts in a bag.” I said.

“Sure. Just give me the peanuts and I will wrap them in a bag.

“Give me anything and I will hand it over to you wrapped in a bag. That’s my job.” — Unit

“What’s your name?”

“People call me Unit here in the Scala store. People from the Haskell store call me return” He smiled.

I also asked him to bag 3 bags of sugar, 1KG each, for my friends. Then I suddenly remembered that they had asked for half a KG of sugar. I proceeded to ask Unit to make it half.

“Sorry sir, I cannot do that. All I know is how to wrap things in a bag. I cannot perform any operation on what is inside.”

I was saddened by the fact that I had to unwrap the bags to half what was inside, then give them back to Unit for re-bagging. Too much work just for what should be a simple task. I wished that someone could help me.

“May I help you?” Another gentleman came smiling. He unpacked each bag, called over a guy who was an expert in halving bags of sugar, and then re-packed it in a bag and handed it over to me

“Now, What’s your name?” I asked him thankfully.

map, people call me map. People in the Haskell store add an extra f before my name. They call me fmap. Funny people.”

I can also replace a bag of sugar with a bag of peanuts. I can even give you a bag of roasted peanuts if you give me a bag of peanuts first.

“Tell me to do anything with what is inside the bag and I will do it. First by unpacking the bag and then repacking it with the new replacement.” — map

Functor

The Bag along with me/map is known as Functor. Basically, the Bag is known as a Functor whenever map is around.

The process of map function on Functor is very well explained by the following illustration taken from: http://adit.io/posts/2013-04-17-functors,applicatives,andmonadsin_pictures.html

1_NAoX33R1asfD4GpaTqp0-w.png

Oh my god, so this is called a Functor! I have been trying to learn this for ages. Tears of happiness rolled down my face.

1_0oLOU0chwBxUiK3BGBrZ3g.jpeg

I took out the laptop and started writing the Bag Functor. Here it is.

In our story:

  • Bag — is a Functor
  • map — is a map function on Functor
  • half — is a function which makes sugar half
  • sugar/peanut — is a generic type A or a type of the content inside Functor
    case class Bag[A](content: A) {
     def map[B](f: A => B): Bag[B] = Bag(f(content))
    }
    
    case class Sugar(weight: Double)
    // the guy who is expert at making sugar half
    def half = (sugar: Sugar) => Sugar(sugar.weight / 2)
    val sugarBag: Bag[Sugar] = Bag(Sugar(1)) //Bag functor of type sugar
    // map is the guy in our story who can perform operations 
    // by unwrapping the bag and then calling respective function
    // and wraps the content back in a bag
    val halfSugarBag: Bag[Sugar] = sugarBag.map(sugar => half(sugar))
    

Where is unit in the above code? Unit in the above code is an apply method provided by case class and hence is not visible. Apply method takes content and returns a bag of content.

def apply(content: A): Bag[A] = new Bag(content)

I was so happy that I understood the concept of Functor.

“But sir, we are called Functor only if we follow certain laws.” Said map. I was little worried now and was listening carefully.

Before I tell you the laws, let me show you a funny guy. Do you see that funny guy. He is called identity. He is so funny that if you hand over 1KG sugar he will give you back 1KG sugar. Essentially he will give back to you, whatever you give to him. Funny fellow. Not the laws.

If you give me 1KG sugar, I will give you 1KG sugar. You give me whatever, I will give it back to you — Identity

def identity[A](x: A): A = x

1. Identity law

When map is called on a Functor with identity function, you get the Functor back.

Functor[X].map(x => identity(x)) == Functor[X]
or
Functor[X].map(identity) == Functor[X]

If you give me a 1KG Bag of sugar and asked me to re-wrap it by calling identity to do his work on the sugar, then you should get a 1KG Bag of sugar — map

This law makes sure that the map functions only applies the function which is passed to it on the contained value and does not perform any other operation of it’s own.

I started verifying the law:

val sugarBag = Bag(Sugar(1))
sugarBag.map(identity) === sugarBag

2. associative law

Let f and g be functions we want to apply on the value contained in functor. Then, calling map with f and then map with g is the equivalent to calling map with g composed with f (g after f or g(f(x))).

_"If you give me a 1KG Bag of peanuts and ask me to roast them then I will give you a bag of roasted peanuts. If you ask me to salt the bag of roasted peanuts I will again unwrap the bag of roasted peanuts, will salt them and will give it back to you. This operation is equivalent to me unwrapping the bag of peanuts roasting them, then salting them and then repacking back."_ — map

Functor[X].map(f).map(g) == Functor[X].map(x => g(f(x))

This law allows us to chain map function calls instead of composing multiple functions and then applying them in a single map operation.

Examples of functors in Scala

  • Option
  • List
  • Future
  • Try
  • Either

What is the benefit of functors

Let’s take an example of Option functor.

Option returns None or Some value. i.e. the container may or may not contain a value.

Let’s say we want to add an Int value to an Option of Int and then multiply it by another number. Without map method the implementation would be something like the following:

val x: Option[Int] = Some(1)
val y: Int = 2
val m: Int = 2
val z = if(x.isDefined) Some((x.get + y) * m) else None

The map method on functor simplifies this logic.

val x: Option[Int] = Some(1)
val y: Int = 2
val m: Int = 2
val z = x.map(a => (a+y) * m)
//or with the help of associative law 
val z = x
  .map(_ + y)
  .map(_ * m)

Monad

I was happy with functors as they were really helpful whenever I visited the Scala supermarket. One day I visited the supermarket and bought a 1KG Bag of Sugar. Then I realized I needed 2KG of sugar. I went to map asking for help. map unwrapped the sugar and asked a guy to double the sugar. The guy who was expert in doubling the sugar was so smart. He not only doubled the sugar but also packed it in a bag and handed over the bag to map. map faithfully packed whatever was given by the expert guy and handed me the bag.

Now I had a bag of 2KG sugar wrapped in another bag. I was a little irritated. If it was already packed in a bag why did map pack it again. Wouldn't it be better to pack the sugar only once?I didn't want to have to unpack two bags to use the sugar.

“Why did you pack the bag into another one if the sugar was already packed” I asked map.

“Sorry sir, I have to follow the laws. I am doing my job. I cannot make any mistakes. My job is to unwrap the bag, give the stuff inside to someone and then repack whatever is given back to me by that person. I don’t care if it is already wrapped or not. I have to wrap it again.” — map

I asked him if is there someone who can flatten it for me? I just want one wrapping.

flatten can help you.” He called over another gentleman named flatten.

“Give me the double wrapped bag and I will flatten it. I will give you sugar wrapped only into one bag” — flatten

“Give me stuff doubly wrapped into a bag and I will return you the same stuff, but with a single wrapping” — flatten

I was so happy to receive sugar in single wrapper as it was more convenient for me.

Do you remember the unit guy. The bag with unit, map and flatten makes a Monad.

It was another shock for me. Monad!, so this is what a Monad is. It’s like a Functor who knows how to flatten.

unit, map , flatten you guys are cool but wouldn’t it be convenient if there was someone who knew how to unwrap/wrap and flatten both. Someone who can do the job of map and flatten” I said.

“Are you taking about me?” I heard a voice.

“My name is flatMap and I do exactly that. I can do both the job of map and flatten.”

The bag with unit and flatMap makes a Monad.

It was time to take out the laptop and code the very first monad. I had already created a Bag functor. I could simply extend the same functor to make it monad by adding flatMap or flatten.

case class Bag[A](content: A) {
  def map[B](f: A => B): Bag[B] = Bag(f(content))

  def flatMap[B](f: A => Bag[B]): Bag[B] = f(content)

  def flatten = content

}
def double = (sugar: Sugar) => Bag(Sugar(sugar.weight * 2))
val doubleSugarBag = sugarBag.map(sugar => double(sugar)).flatten
or
val doubleSugarBag = sugarBag.flatMap(sugar => double(sugar))

Yes we are like Functor but we also know how to flatten the double wrapping. “Do you have to follow certain laws like Functors?” I asked.

“Yes we have to obey following laws to call our self a Monad”

1. Left-identity law

unit(x).flatMap(f) == f(x)

i.e.

Bag(Sugar(1)).flatMap(double) == double(Sugar(1))
//or to be more explicit the same statement could be written as
Bag.apply(Sugar(1)).flatMap(z => double(z)) == double(Sugar(1))

Here

  • unit is apply function on the monad.
  • f is the double function which doubles the quantity
  • x is 1KG Sugar i.e. Sugar(1)

So the Bag Monad fulfills the first law. Hurrah.

2. Right-identity law

Monad[X].flatMap(unit) == Monad[X]

i.e.

Bag(Sugar(1)).flatMap(Bag.apply) == Bag(Sugar(1))

Bag monad fulfills the second law as well.

3. Associativity law

m.flatMap(f).flatMap(g) == m.flatMap(x ⇒ f(x).flatMap(g))

i.e.

Bag(Sugar(1)).flatMap(double).flatMap(tripple) == Bag(Sugar(1)).flatMap(x ⇒ double(x).flatMap(tripple))

So the Bag monad fulfils all the laws.

List of some important Monads in Scala

  • List
  • Option
  • Future
  • Try

Hope you loved the story. Clap for it if you liked it. Don’t forget to follow me.

Originally published on medium.com

Author's avatar
Sujit
    Java
    Scala
    JavaScript
    HTML
    CSS
    Groovy
    Shell
    ApacheConf

Related Jobs

Related Issues

WorksHub / client
  • Open
  • 0
  • 0
  • Intermediate
  • Clojure
  • $50
WorksHub / client
WorksHub / client
  • Started
  • 0
  • 4
  • Intermediate
  • Clojure
  • $100
WorksHub / client
  • 1
  • 0
  • Intermediate
  • Clojure
WorksHub / client
  • 1
  • 0
  • Intermediate
  • Clojure
WorksHub / client
  • 1
  • 0
  • Intermediate
  • Clojure
WorksHub / client
  • Open
  • 0
  • 0
  • Intermediate
  • Clojure
cosmwasm / wasmd
  • 1
  • 2
  • Intermediate
  • Go
cosmwasm / wasmd
  • Started
  • 0
  • 1
  • Intermediate
  • Go
cosmwasm / wasmd
  • Started
  • 0
  • 1
  • Intermediate
  • Go

Get hired!

Sign up now and apply for roles at companies that interest you.

Engineers who find a new job through Functional Works average a 15% increase in salary.

Get Started with