Here’s a simple value:
And we know how to apply a function to this value:
Simple enough. Lets extend this by saying that any value can be in a context. For now you can think of a context as a “box” that you can put a value in:
Now when you apply a function to this value, you’ll get different results depending on the containing box. This is the idea that Functors, Applicatives, Monads, Arrows etc are all based on.
When a value is wrapped in a box, you can’t apply a normal function to it:
This is where map (fmap in Haskell) (<$> in Haskell) comes in. map is from the street, map is hip to containing box. map knows how to apply functions to values that are wrapped in a box. For example, suppose you want to apply a function that adds 3 to the wrapped 2 and assume there’s a map function exist:
Just what is a Functor, really?
A functor is any type that defines how map (fmap in Haskell) works.
Regardless it iterates each values, here’s what is happening behind the scenes when we write .map(plus3):
So then you’re like, alright map, please apply plusThree to a empty container.
It does end up with nothing. To illustrate it doesn’t apply a function at all, we tweak the function a bit:
Like Morpheus in the Matrix, map knows just what to do; you start with None, and you end up with None! map is zen.
In this case, Promise is also a functor. It has an interface where we can apply a function to its wrapped value.
Here’s another example: what happens when you apply a function to a list?
When an Array has multiple values, it naturally applies the function to each value and replaces the array with new values.
Okay, okay, one last example: what happens when you apply a function to another function?
Here’s a function:
Here’s a function applied to another function:
The result is just another function!
Applicatives take it to the next level. With an applicative, our values are wrapped in a container, just like Functors:
But our functions are wrapped in a container too!
Says we have two wrapped values and we want to add them up:
What if we put a curried add function in map if the container is a functor ?
Now we have a wrapped curried function, how can we apply it to another wrapped value ? Assume ap (<*> in Haskell) is a function that can apply the function contents of a functor to an wrapped value.
If the we have multiple wrapped functions and values, we might expect this to happen:
Applicative pushes Functor aside. “Big boys can use functions with any number of arguments,” it says. “Armed with curry function, map (<$> in Haskell) and ap (<*> in Haskell), I can take any function that expects any number of unwrapped values. Then I pass it all wrapped values, and I get a wrapped value out! AHAHAHAHAH!”
How to learn about Monads:
- Get a PhD in computer science.
- Throw it away because you don’t need it for this section!
Monads add a new twist.
Functors apply a function to a wrapped value:
Applicatives apply a wrapped function to a wrapped value:
Monads apply a function that returns a wrapped value to a wrapped value.
Monads have a function flatMap (liftM in Haskell) (>>= in Haskell pronounced “bind”) to do this.
Suppose half is a function that only works on even numbers:
What if we feed it a wrapped value?
We need to use flatMap(>>= in Haskell) to shove our wrapped value into the function.
You can also chain these calls:
You might have the question that why don’t we just use map. I am glad you ask ! This is because half returns a wrapped value. If we use map, we are putting a container inside another container.
But why do we return a wrapped value in half ? In this case, we use the wrapped value to represent potential empty value and prevent the following calls in the chain from being executed if it’s empty. Let’s look at an other example:
Here’s a simple http request utility where we wrap the response in a promise:
With above utility, we try to make sequential calls to get the info of the first connection of contact 1.
The onFullfilled lambda can return either a value or a promise (thenable). When it returns a value, then is like a map function. When it returns another promise, then acts like a flatMap. The resolution state of returned promise will control the following operations and yield the eventual value (contact info of the first connection of contact 1.).
So, we can see the following traits:
- A functor is a type that implements map.
- An applicative is a type that implements ap.
- A monad is a type that implements flatMap.
- Array implements map, so it’s a functor.
- Promise implements map and flatMap through then, so it is a functor and a monad.
What is the difference between the three?
- functors: you apply a function to a wrapped value.
- applicatives: you apply a wrapped function to a wrapped value.
- monads: you apply a function that returns a wrapped value, to a wrapped value.
So, dear friend (I think we are friends by this point), I think we both agree that monads are easy and a SMART IDEA(tm). Now that you’ve wet your whistle on this guide, why not pull a Mel Gibson and grab the whole bottle. Check out LYAH’s section on Monads. There’s a lot of things I’ve glossed over because Miran does a great job going in-depth with this stuff.