Functor Vs Closure

An advantage of typical lambda expressions is that they implicitly close over their defining environment, whereas FunctorObjects require an explicit specification of environment.

The problem...

Imagine we're coding in Java and need to sort a list of numeric strings...

 List<String> list = Arrays.asList("10", "1", "20", "11", "21", "12");
...in numeric order.

Example 1 -- FunctorObject

In the following example, a FunctorObject class called NumStringComparator is defined -- it implements the appropriate comparison operation -- and an instance of it is passed to the built-in Collections.sort() method:

 private static class NumStringComparator implements Comparator<String> {
        public int compare(String str1, String str2) {
            return Integer.valueOf(str1).compareTo(Integer.valueOf(str2));
        }
 }

List<String> list = Arrays.asList("10", "1", "20", "11", "21", "12");

Collections.sort(list, new NumStringComparator());
Example 2 -- FunctorObject with context

Now let's imagine that the compare() method needs to make reference to the external environment. For example, it needs to access some context (called Context) that affects the run-time collating order of the sort operation. Adapting the example, it would probably look like this:

 private static class NumStringComparator implements Comparator<String> {
private Context context;
public NumStringComparator(Context context) {
this.context = context;
}
        public int compare(String str1, String str2) {
            return Integer.valueOf(str1).compareTo(Integer.valueOf(str2)) * context.compareAdjustment();
        }
 }

List<String> list = Arrays.asList("10", "1", "20", "11", "21", "12"); Context context = new Context();

Collections.sort(list, new NumStringComparator(context));
Note that we've had to explicitly pass the Context to the "new NumStringComparator" functor object via its constructor. That's an illustration of the general FunctorObject approach, and in some ObjectOriented languages that's all we've got.

Example 3 -- LambdaExpression

Using Java 8 lambda expressions (i.e., AnonymousFunctions), the equivalent to the first example can be:

 List<String> list = Arrays.asList("10", "1", "20", "11", "21", "12");

Collections.sort(list, (str1, str2) -> Integer.valueOf(str1).compareTo(Integer.valueOf(str2)) );
Example 4 -- LambdaExpression with Closure

Using a lambda expression, the equivalent to Example 2 would be:

 List<String> list = Arrays.asList("10", "1", "20", "11", "21", "12");
 Context context = new Context();

Collections.sort(list, (str1, str2) -> Integer.valueOf(str1).compareTo(Integer.valueOf(str2)) * context.compareAdjustment() );
A lambda expression can reference -- i.e., close over -- its defining environment, so using 'context.compareAdjustment()' is simply a matter of specifying it.

Example 5 -- Using anonymous classes to emulate lambda expressions and 'final' to simulate closures.

To be fair to older (pre version 8) Java, it does let us come half-way to lambda expressions by using anonymous classes. We (almost) get closures as long as we can get away with declaring 'context' as 'final', in other words, if we can guarantee that it won't change throughout the lifetime of the FunctorObject. So, we can do the following:

 List<String> list = Arrays.asList("10", "1", "20", "11", "21", "12");
 final Context context = new Context();

Collections.sort(list, new Comparator<String>() { public int compare(String str1, String str2) { return Integer.valueOf(str1).compareTo(Integer.valueOf(str2)) * context.compareAdjustment(); } });
Obviously, it's not as simple and clean as Example 4, above.


Discussion of practical usage at PageAnchor customized-collation-01 under EvidenceDiscussion.


CategoryJava CategoryFunctionalProgramming


EditText of this page (last edited September 13, 2014) or FindPage with title or text search