Functors in Java and beyond
Functors are defined as a function encapsulated in an object. In most cases, the function’s parameters can be set and the result retrieved using the common accessor pattern, such as setParameter1(Object value) or getResult(). The fact that these functions are encapsulated by real objects is also the reason for its greatest benefit: the use of many design patterns including structural ones such as the Decorator pattern and behavioral ones such as the Visitor pattern.
And in code:
Functors in software design
While the classic object oriented design encourages inheritance and method overloading to achieve code reuse and code specialization, the functors’ design promote another approach, of composition. Functors are created to accomplish simple tasks and are later connected together, by connecting one functor’s result to another’s parameter, to satisfy a greater goal of the application. By combining different functors together, one can in fact create a proxy functor, which takes parameters for its encapsulated functors, and returns a result which is in fact the result of the last functor in its composition. As an example, take the classic genetic algorithm cycle of population initialization, evaluation, selection, breeding, evaluation again, replacement of older generation and back to selection. Each of these can be defined by a functor, and the output of each is connected as the input of the next. In fact, the main iteration (selection, breeding and evaluation) can be defined by a functor as well, and then called iteratively until the stopping condition is reached. The best Java functors’ framework I could find out there is Java Generic Algorithms, or JGA. It offers the generic functor, and on top of that offers the three most common functors, being Generator (no parameters), UnaryFunctor (single parameter) and BinaryFunctor (two parameters). Functor composition is available through the ability to bind any parameter of a functor to any other functor or constant value. To continue our previous example, let’s composite the main GA iteration using JGA:
And in code:
UnaryFunctor<Population, Individual> selector = …;
BinaryFunctor<Individual, Individual, Individual> breeder = ….;
UnaryFunctor<Individual, Individual> evaluator = …;
UnaryFunctor<Population, Individual> composedBreeder =
breeder.compose(selector, selector);
UnaryFunctor<Population, Individual> composedEvaluator =
evaluator.compose(breeder);
return composedEvaluator;
Later on, this generated functor can be used and composed with other functors, such as a functor to iteratively produce new offsprings for the new generation and a functor to check for the end-condition. Speaking of the end-condition functor, these special functors with a result of a boolean are sometimes referenced to as Predicates, and are more widely known and used.
Comments (6)