Franck Mahon (modified) @ Flickr.com CC 2.0

Today we are going to disassemble Functor as another concept in Category Theory widely used in Functional Programming. The idea of a Functor seems trivial and many people when asked what it is, quickly respond that it’s ‘something that can be mapped over’ — this is true but it’s worth to know the full picture, what is actually mapped and how. This is where Category Theory (and of course Bartosz Milewski) comes to the rescue.

Once again I highly recommend Bartosz Milewski’s video series about Category Theory for Programmers on YouTube as well as his blog post series about those concepts.

What is a Functor

Mathematically speaking, Functor is a very simple idea — it is mapping of objects from one Category to another Category. This sentence closely resembles the ‘something that can be mapped over’ idea repeated over and over in a programming world. But the story doesn’t end here.

We know that Category is objects and morphisms (arrows), we also know that Category is about composition and identity and Category’s structure is defined through composition. Functor allows us to map Categories and preserve the structure. To preserve the structure, we need to map not only the objects but the morphisms too. In other words, we can think of a Functor as a set of functions mapping a set of arrows from one Category to the set of arrows in another.

A Functor is kind of mapping of objects and morphisms that preserves composition and identity.

We have two Categories: A and B . In Category A we have two objects a and b with morphism f . Our Functor is a mapping of objects a and b to Fa and Fb and mapping of morphisms, in this case single morphism: f to Ff .

Continuing on a structure of Category, if you can compose two morphisms in one Category you can compose them in another when using a Functor:

We have a morphism in Category A between a and c ( f · g ) which is a composition of morphisms f and g , and in our Category B we have mapped the objects and morphisms correspondingly with a Functor so that

F(g · f) = Fg · Ff

One more requirement for our Functor is to preserve identity. In Category A we know that id a = a so our mapped version should look like the following:

Fid a = Fa or actually F(id(a)) but we can shorten it to Fa

Ok, so that’s the theory, now let’s see some examples.

Example

One of the simplest cases for a Functor explanation is something like Scala’s Option . Let’s say we want to map object of type A to an object of type Option[A]

Staying for a little bit longer in Category Theory mode but with the Option example, let’s say that we have some objects and morphism of objects in one Category and we want to map them to another category with a Functor.

In the example above we are mapping object a to Option[a] and object b to an Option[b] , we are also mapping a morphism f with a function fmap . The fmap name is used here on purpose as we all know that real Scala’s Option has no fmap function available (unless used with cats implicit Functor type class but we get back to this in a short while)

When mapping a concrete object with Option we can have two possible values, Some or None . So our Functor would need to execute the following:

Mapping None :

Mapping Some(..) :

Mapping morphism f :

Preserving composition:

Preserving identity:

As you can see, our Functor is a set of functions mapping objects and morphism from one Category to another. The case where we map the whole morphism f between objects a and b is called lifting as you will see shortly in the Cats Functor section below.

In Scala, our fmap from above definitions would like a function of two arguments, the first one being the function we want to apply and the second one being a Functor instance (Option, List etc.). This of course doesn’t work like that in Scala. In Scala, by convention, map() is invoked upon an instance of the class and takes only one argument — the function we want to apply, the principles stay the same.

If we think about Functor examples, we know that Functor is something that can act on types, like our before-mentioned Option[A] or List[A] , so except for a Functor being a `set of functions` we can also think about Functor as a type constructor that supports fmap operations, or, in addition, we can also think about Functor as a container.

Important note: do not confuse word fmap used here with Scala’s flatMap . fmap name is borrowed here from Haskell and it’s about mapping, like map in Scala is, and not about mapping and flattening the result like flatMap.

Cats Functor

Among many type classes available in Cats for different concepts borrowed from Category Theory we have a Functor too.

Functor is a type class that abstracts over type constructors that can be map ‘ed over. Examples of such type constructors are List , Option , and Future .

As you can see below, Functor map looks like the classic map function and lift will lift up a function call to its Functor equivalent (mapping morphisms mentioned earlier):

For all the Category Theory lovers there is also an fmap implementation:

as you can see it has the same signature as our old’n good map function.

Usage Examples

Possibly most of us are used to Functor’s map function to work with effects. For example we can map on Future to apply some pure function to a value wrapped in our effect (Future) without needing to unwrap and wrap the value back into the effectful wrapper. In other words, we are working on a value without leaving the effect context. This is very useful but we can get even more from a simple Functor concept.

Composing with nested data types

The most common example for the Functor you will possibly see is about mapping on nested data types. With regular Scala approach, you would need to use map inside other map calls to execute some pure function on the value nested deep inside such a structure, for example:

Whereas with Functor available for Option and List with cats.implicits._ you can do the following:

or with Nested Functor wrapper:

It becomes even more useful when the structure we want to use is more complex:

With a chain of compose calls and Functor implicits provided for List and Option we can execute functions on deep nested structures.

If the Functor implicit is not available for our structure we can create our own:

There is no Functor available for Map so with a simple type trick we can create one for ourselves and compose it to have a Functor which we can use for our complexContainer .

As you can see we can use Functors made available for you through cats.instances.* , create Functors for the existing container types in Scala as well as compose them together to work with more complex structures.

Watch this space for more Category Theory learning materials and Cats implementation examples.