In general, a Functor is a concept originating from mathematics, to be exact from a part of mathematics called category theory. In my very first article, I described in detail a similar concept known as Monad. Here I want to continue that thread, so I will try to give you some more insight into Functors — what they are, how they work, and what the theory behind them is. I will also implement a simple functor to better understand how they work and why using them may be a clearer solution.
The source code for this article is available in GitHub repository.
Why Even Care About Functors?
Firstly, functors are probably the simplest of commonly known functional containers, and understanding their mechanics can help while working with more complex containers like monads or applicatives.
Secondly, it is quite a handy concept, especially helpful when you need to perform operations over some value inside the container.
Finally, unlike monads, which have quite a few counterparts in Java, functors lack a good builtin equivalent, at least as far as I could check, so it may come in handy someday to know the concept. It can make your life easier.
What Is a Functor?
As I mentioned above, a Functor is a concept from category theory and represents the mapping between two categories. In software, functors can be viewed as a util class that allows us to perform a mapping operation over values wrapped in some context. Despite being quite easy to implement with the use of a simple interface, we must keep in mind that to honestly call our piece of code a functor, we have to fulfill laws applying to functions in category theory – more about this below.
Also, contrary to monads from the first article, there are no good builtin counterparts of functors in Java and other modernday programming languages. Probably the best counterpart can be found in the Scala library called Cats.
Functor Laws
As most of the concepts from mathematics moved to programming, Functors also have some theoretical background behind them. In the case of functors, a part of this background are their laws, namely Identity and Associativity. Below, I will try to present them in a more illustrative way with the use of a ReferntailFunctor class of my own making.
Assumptions about functions used in the associativity part:
 f is a function mapping from type T to type R
 g is a function mapping from type R to type U

Identity:
Mapping values inside the functor with the identity function should always return an unchanged value.
ReferentialFunctor<Integer> identity = new ReferentialFunctor<>(x).map(Function.identity()); assert identity.valueEquals(x);

Associativity:
This law is the same as the one in case of monads so it states that in the chain of function applications, it should not matter how functions are nested.
ReferentialFunctor<Long> leftSide = new ReferentialFunctor<>(x).map(f).map(g); ReferentialFunctor<Long> rightSide = new ReferentialFunctor<>(x).map(f.andThen(g)); assert leftSide.equals(rightSide);
Creating a Functor
Similar to a monad, we will start with the creation of a parameterized type M<T>, a wrapper for the value of type T inside. However, unlike in the case of a monad, we will have to implement only one method:
After expelling the mathematical context behind a Functor and what method it required, we can start implementing it.
Implementing a Functor
import org.pasksoftware.functor.Functor;
import java.util.function.Function;
public class WrapperFunctor<T> implements Functor<T> {
private final T value;
public WrapperFunctor(T value) {
this.value = value;
}
@Override
public <R> WrapperFunctor<R> map(Function<T, R> f) {
return new WrapperFunctor<>(f.apply(value));
}
// For sake of asserting in Example
boolean valueEquals(T x) {
return value.equals(x);
}
}
Here we are with ready Functor implementation, time to explain what happened in these 20 lines of code.
What Exactly Happened Above
The base of our implementation is the parameterized class with the immutable field named “value“, which is responsible for storing our value. Then, we have a constructor, which actually makes it possible to instantiate our small functor implementation.
Next, we have the meat of this snippet, the map method that will guarantee that we are able to fulfill the required conditions in the form of functor laws.
With all the code described, it is time for an example of functors at work.
import java.util.function.Function;
public class Example {
public static void main(String[] args) {
int x = 2;
Function<Integer, String> f = Object::toString;
// Task: mapping over the value inside the container object.
// Nonfunctor
Wrapper<Integer> wrapper = new Wrapper<>(x);
String mappedValue = f.apply(wrapper.value);
// One liner  Wrapper<String> mappedWrapper = new Wrapper<>(f.apply(x));
Wrapper<String> mappedWrapper = new Wrapper<>(mappedValue);
// Functor
WrapperFunctor<String> mappedWrapperFunctor = new WrapperFunctor<>(x).map(f);
assert mappedWrapperFunctor.valueEquals(mappedWrapper.value);
System.out.println("Values inside wrappers are equal");
}
}
In the above snippet, you could see why functors can be useful apart from their somewhat simple structure. You can notice that performing the same operations without using functors required some changes in our API design and some additional boilerplate to make it more readable.
Summing up
Functors can be quite useful even besides their simple structure. It is the simplest of commonly known containers from category theory and knowing how it works can help you further in your journey. What is more, a functorbased approach can provide a more descriptive and clearer solution for operating on values inside containers of any type. Thank you for your time.
If you enjoyed reading about Functors, I can recommend you my article about Monads.