DEV Community

Lomig
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"]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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]
Enter fullscreen mode Exit fullscreen mode

So, what about Functors?

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)
Enter fullscreen mode Exit fullscreen mode

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

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

A monad is just a monoid in the category of endfunctors

But that's for the next article!

Top comments (0)