Lomig

Posted on

# Monads explained to my team — Part 2: Endofunctors

After talking about what monoids are in Part 1, and discovering that it was just a smart word for very simple things, let's discover the marvellous world of functors in this second part!

## Fancy words for simple concepts

Developers uses functors everyday unknowingly, so let's figure out what this is all about.

## `#map`

We all know `#map`, a method that we use and love as Rubyists.
It allows us to take a structure of any kind (in terms of Ruby, an `Enumerable`), and from within this structure, apply the same function to each of its elements.

``````savings_in_€ = [24.30, 350.90, -105.00]

#Brexit!
savings_in_£ = savings_in_€.map { |amount| (amount * 0.85).round(2) }
#=> [20.66, 298.27, -89.25]

savings_in_£.map { |amount| "£#{amount}" }
#=> ["£20.66", "£298.27", "£-89.25"]
``````

## The rules of `#map`

Of course, `#map` could be called anything else — the important part of this method is not its name, but the fact that it must follow some rules to be considered a real `map`!

Those are the two rules:

• Rule of identity
``````any_array.map { |x| x } == any_array
``````
• Rule of composition
``````def multiply_by_two(x) = 2 * x
def add_three(x) = x + 3

a = [2, 5, 7].map { |x| multiply_by_two(add_three(x)) }
b = [2, 5, 7].map { |x| add_three(x) }
.map { |x| multiply_by_two(x) }

a == b # [10, 16, 20]
``````

A functor is:

• a structure that is `mapable`
• that is polymorphic (ie that can contain any type)

In Ruby, it means that:

• Arrays are functors
• ✅ can map internal elements
• ✅ respect map rules from above
• Hashes are not functors
• ✅ can map internal elements
• ❌ does not respect map rules

## And Endofunctors, then?

An endofunctor is a functor whose `#map` function will return a structure with the same type.

In Ruby, it means that Arrays are endofunctors
* ✅ takes an `Array`, and returns an `Array`

## As a Rubyist, why do I care?

Functors, and more particularly endofunctors, allow for function composition and make it safe to chain any number of mapping:

``````Array.map { |x| x * 17 }
.map { |x| x / 3 }
.map(&:round)
.map(&:to_s)
``````

## Ok, but for God's sake, what's the relevance with Monads?

Well, obviously, it's the definition of a monad!

But that's for the next article!