Apache Commons logo Commons Functor

Examples

This page contains basic examples using Predicates, Functions, Procedures, Generators, Ranges and Aggregators. There are also examples using composition and more practical examples at the bottom of this page.

Predicates

Predicates are functors that return a boolean value. The following snippet of code shows how to use a Predicate that says whether a number is even or not.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);

Predicate<Integer> isEven = new Predicate<Integer>() {
    public boolean test(Integer obj) {
        return obj % 2 == 0;
    }
};

for( Integer number : numbers ) {
    if (isEven.test(number)) {
        System.out.print(number + " ");
    }
}

The code above produces the following output: 2 4

Functions

Functions are functors that return an Object value. The following snippet of code shows how to use a Function that doubles a value.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
        
Function<Integer, Integer> doubler = new Function<Integer, Integer>() {
    public Integer evaluate(Integer obj) {
        return obj * 2;
    }
};

for( Integer number : numbers ) {
    Integer value = doubler.evaluate(number);
    System.out.print(value + " ");
}

The code above produces the following output: 2 4 6 8

Procedures

Procedures are functors that do not return anything. In the snippet of code below you can find an example that prints the value passed to the Procedure.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);

Procedure<Integer> print = new Procedure<Integer>() {
    public void run(Integer obj) {
        System.out.print(obj + " ");
    }
};

for( Integer number : numbers ) {
    print.run(number);
}

The code above produces the following output: 1 2 3 4 .

Generators

Apache Functor includes other objects that you can can use to code in a less imperative way, like Generators. In the following example, we create an Integer Generator that generates integers from 1 to 4 (the right argument is non-inclusive). The generator is wrapped within a Filtered Generator that applies the isEven predicate to each integer generated by the former generator. Finally, we execute a Composite Unary Procedure that uses a function to double the value of the integer before printing it.

Generator<Integer> integerGenerator = new IntegerRange(1, 5); // inclusive, exclusive
    
UnaryPredicate<Integer> isEven = new UnaryPredicate<Integer>() {
    public boolean test(Integer obj) {
        return obj % 2 == 0;
    }
};

FilteredGenerator<Integer> filteredGenerator = 
        new FilteredGenerator<Integer>(integerGenerator, isEven);

UnaryFunction<Integer, Integer> doubler = new UnaryFunction<Integer, Integer>() {
    public Integer evaluate(Integer obj) {
        return obj * 2;
    }
};

UnaryProcedure<Integer> print = new UnaryProcedure<Integer>() {
    public void run(Integer obj) {
        System.out.print(obj + " ");
    }
};

CompositeUnaryProcedure<Integer> compositeProcedure =
        new CompositeUnaryProcedure<Integer>(print);

filteredGenerator.run(compositeProcedure.of(doubler));

The lines package demonstrates a functional approach to IO using Generators and the Algorithms class.

Ranges

Using Ranges you are able to create a series of elements of a certain type. The distance between each element is called step. And the left and right limits are endpoints.

By default, in numeric ranges the left value is inclusive and the right is exclusive. The range below creates a series of Integers between 0 and 10, with a default step of 1.

// [0, 10), 1 = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
IntegerRange range = new IntegerRange(0, 10);

The step value can be changed, altering the distance between each element in the range. As shown below.

// [0, 10), 2 = 0, 2, 4, 6, 8
IntegerRange range = new IntegerRange(0, 10, 2);

It is possible, too, to define the bound type of each endpoint. It is similar to mathematical intervals, where a closed endpoint means that the value is included in the series of elements in the range. On the other hand, an open endpoint means that the value is not included.

// (0, 10], 2 = 2, 4, 6, 8, 10
IntegerRange range = new IntegerRange(0, BoundType.OPEN, 10, BoundType.CLOSED, 2);

A Range is also by nature a Generator, so you can use it for executing procedures for each of its elements.

// [0, 10), 1 = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
IntegerRange range = new IntegerRange(0, 10);
UnaryProcedure<Integer> printProcedure = new UnaryProcedure<Integer>() {
    
    public void run(Integer obj) {
        System.out.print(obj + " ");
    }
};
range.run(printProcedure);

The code above produces the following output: 0 1 2 3 4 5 6 7 8 9

Aggregators

There are some code snippets / examples for the org.apache.commons.functor.aggregator package available on this page. Also, to exemplify the usage of the Aggregator classes, there are code examples in the test section.

First such set of example involves the usage of the nostore Aggregator. Code can be found in org.apache.commons.functor.example.aggregator.nostore. This shows how can you use an aggregator which doesn't store the data series and processes them on the fly. Also, there are examples provided which show how can you implement your own aggregation function to be used with this Aggregator type.

For using an Aggregator which stores the data series in a list, examples are in org.apache.commons.functor.example.aggregator.list. This shows how can you use the ArrayList-backed aggregator or provide your own List-based implementation. Also, there are examples provided which show how can you implement your own aggregation function to be used with this Aggregator type.

Reuse Through Composition

The Functor package, and more generally, a functional approach to program design, supports a powerful technique for balancing behavior specialization and code reuse.

Traditional Object Oriented design suggests inheritence as a mechanism code reuse, and method overloading as a mechanism for specialization. For example, one defines a general purpose, perhaps even abstract class, say AbstractList, and then extend or specialize this parent via subclasses, inheriting some behaviors and overloading others.

Functors encourage another, complementary approach to code reuse and behavior specialiazation: composition. Following a compositional design, we create a number of simple objects and then combine them to create more complex behaviors. For example, the Commons Pool component defines an ObjectPool type that maintains a collection of pooled objects, but delegates to a PoolableObjectFactory to create, validate and destroy the objects to be pooled. Arbitrary ObjectPool implementations can be composed with arbitrary PoolableObjectFactory implementations in order to create new types of pools.

Let's see an example that combines the three functors seen here so far. In this example, we will use the functors that we created so that for each even number found, it will double its value and will print the new value.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);

Predicate<Integer> isEven = new Predicate<Integer>() {
    public boolean test(Integer obj) {
        return obj % 2 == 0;
    }
};

Function<Integer, Integer> doubler = new Function<Integer, Integer>() {
    public Integer evaluate(Integer obj) {
        return obj * 2;
    }
};

Procedure<Integer> print = new Procedure<Integer>() {
    public void run(Integer obj) {
        System.out.print(obj + " ");
    }
};

for( Integer number : numbers ) {
    if(isEven.test(number)) {
        print.run(doubler.evaluate(number));
    }
}

The code above produces the following output: 4 8

The FlexiMap example applies this design to java.util.Map, demonstrating how "pluggable" functors can be applied to a generic Map structure in order to introduce new behaviors. The map package is a more complete example of this, implementing a number of the Commons-Collections Maps derived from a base FunctoredMap.

Generators

Apache Functor includes other objects that you can can use to code in a less imperative way, like Generators. In the following example, we create an Integer Generator that generates integers from 1 to 4 (the right argument is non-inclusive). The generator is wrapped within a Filtered Generator that applies the isEven predicate to each integer generated by the former generator. Finally, we execute a Composite Procedure that uses a function to double the value of the integer before printing it.

Generator<Integer> integerGenerator = new IntegerRange(1, 5); // inclusive, exclusive
    
Predicate<Integer> isEven = new Predicate<Integer>() {
    public boolean test(Integer obj) {
        return obj % 2 == 0;
    }
};

FilteredGenerator<Integer> filteredGenerator = 
        new FilteredGenerator<Integer>(integerGenerator, isEven);

Function<Integer, Integer> doubler = new Function<Integer, Integer>() {
    public Integer evaluate(Integer obj) {
        return obj * 2;
    }
};

Procedure<Integer> print = new Procedure<Integer>() {
    public void run(Integer obj) {
        System.out.print(obj + " ");
    }
};

CompositeProcedure<Integer> compositeProcedure =
        new CompositeProcedure<Integer>(print);

filteredGenerator.run(compositeProcedure.of(doubler));

The lines package demonstrates a functional approach to IO using Generators and the Algorithms class.

Aggregators

There are some code snippets / examples for the org.apache.commons.functor.aggregator package available on this page. Also, to exemplify the usage of the Aggregator classes, there are code examples in the test section.

First such set of example involves the usage of the nostore Aggregator. Code can be found in org.apache.commons.functor.example.aggregator.nostore. This shows how can you use an aggregator which doesn't store the data series and processes them on the fly. Also, there are examples provided which show how can you implement your own aggregation function to be used with this Aggregator type.

For using an Aggregator which stores the data series in a list, examples are in org.apache.commons.functor.example.aggregator.list. This shows how can you use the ArrayList-backed aggregator or provide your own List-based implementation. Also, there are examples provided which show how can you implement your own aggregation function to be used with this Aggregator type.

Code Katas

"Pragmatic" Dave Thomas has been blogging a series of programming exercises he calls Code Katas. These exercises are intended to provide "practice sessions" that allow programmers to hone their craft. The notion is borrowed from the practice of Karate, where, in Dave's words "a kata is an exercise where you repeat a form many, many times, making little improvements in each".

Here we use several of Dave's Code Katas to explore the Commons-Functor library.

Kata One: Supermarket Pricing
Dave's Kata One asks how one might implement supermarket pricing rules, like "three for a dollar" or "buy two get one free". By encapsulating tiny bits of logic, functors provide a useful solution to this problem, as illustrated in the SupermarketPricingExample.
Kata Two: Binary Chop
Kata Two asks us to create several different implementations of the binary search algorithm, which once you get past three or four implementations, is more difficult that it sounds. TestBinaryChop presents several implementations, with functor and non-functor variations.
Kata Four: Data Munging
Kata Four asks us to explore extreme reuse. Our DataMunger allosubsubsectionws for very small implementations of the weather and soccer (football) parsers.

A Quicksort Implementation

The Quicksort example presents an implementation of the Quicksort sorting algorithm written in a functional programming style using Commons Functor.