Practical introduction to Functional Programming with JS

AndreaZanin
93.5K views

Open Source Your Knowledge, Become a Contributor

Technology knowledge has to be shared and made accessible for free. Join the movement.

Create Content

Functors

Remember when we talked about map? We only used map on arrays, but you can actually implement map on other data structures, e.g. trees, streams, promises...

Any type that has a map function is a functor.

You should have understood the concept of map by now, but if you a real nitpicker, you will now ask "How do we define map?". One of the best definitions is given by haskell's functor laws; for those of you who are interested, I will translate them into JavaScript:

// identity
myFunctor.map( x => x) === myFunctor

// composition === chaining
myFunctor.map( x => f(g(x)) ) === myFunctor.map(g).map(f)

In the code above, I used === to keep it simple, but I should have checked for deep equality. If you don't know what this means, don't worry, you should be able to understand the meaning anyway. Nonetheless, if you want to learn more about it, this StackOverflow thread is a good place to start.

The advantage of using a functor is that the container is now abstracted away. E.g., in streams, we don't need to care about asynchronous data handling, we can just use a stream like an array.

Another cool feature of functors is that you can chain map calls, because the map function returns another functor.

Chaining

So why don't we create our own functor?

The Tuple Functor

Monads

Giving a definition of monad is somewhat tedious and requires a bit of theory, so we are first going to build an intuition for them through examples.

Array Monad

Let's write a function that duplicates every item in an array (e.g. [1,2,3] --> [1,1,2,2,3,3]).
Let's first try with map:

map duplicate

Running the code, you can see that the result is not quite what we wanted: [[1,1],[2,2],[3,3]]. Wouldn't it be great if we had a function that automatically "flattened" the array we returned into [1,1,2,2,3,3]? That's flatMap!

flatMap duplicate

The flatMap function was implemented by me, because it's not provided by javascript yet; we'll deal with the details later.

As you can see, flatMap's callback returns another monad and flatMap's job is handling the unpacking. If you are interested in the details of the implementation, here's my code:

flatMap implementation

Although the array's implementation of flatMap is very interesting and useful, it's not the only one: we can, for example, use flatMap to maintain knowledge of previous states of a system. As always, let's start with an example.

Gambler Monad

Let's say we want to create a model for a gambler: the gambler plays multiple times at the roulette; sometimes he wins, sometimes he loses. But if he goes bankrupt, he gets kicked out of the casino and cannot recover. We want to simulate a number of games and see if at the end the gambler still has money or not.

We cannot simply add up wins and losses, because we would ignore intermediate states. The following is a wrong solution:

map implementation

What we need is a model that stops computing the transactions after he went bankrupt, like the following:

map implementation

This time, we used flatMap to remember past states, but again the callback returned a new monad and flatMap handled the unwrapping of the original monad content.

Wrapping up

Monads are a broad definition, but they all share one characteristic: they implement flatMap, which in term is useful to maintain knowledge of the context in which a function is being executed.

Open Source Your Knowledge: become a Contributor and help others learn. Create New Content