Skip to main content

Functional objects made easy with tiger

Posted by alexwinston on January 19, 2005 at 6:49 AM PST

After reading CrazyBob's article about sudo closures in java some time ago I made
it a point to familiarize myself with this particular idiom as well as others that
are often talked about but rarely used within the java community. Unfortunately
some time passed and as most things this todo slipped my mind until I stumbled
upon this article.

The article was rather short but provided practical examples of how to use
commons-collections to express functional object constructs within java. I read
the article with great anticipation and began to explore commons-collections
and even attempted to port CrazyBob's simple example. This is however where the
difficulty began. The library seemed a bit awkard and verbose and I found myself
wanting features found in ruby and oft talked about on Brian McCallister's
blog. In an attempt to bridge the
gap between languages that offer true functional object support and java I fired
up my trusty ide and set off to find a solution.

I felt that commons-collections abtraction of Predicates and Closures was a start
but something else was missing, not to mention the verbosity of the implementation.
In order to alleviate some of the verbosity I decided to use features provided
in jdk 1.5 and build upon the simple abstractions provided by commons-collections.

Now I do not puport to be an english scholar by any means but when I began to
think somewhat abstractly about predicates and closure sentence structure
began to come to mind. Specifically I was envisioning the preposition of subjects
for predicates against closures. That may or may not actually make any meaningful
sense to an english scholar but it made sense in my head and so I set off to
implement it accordingly.

To begin I created interfaces for Predicate(s) and Closure(s) as illustrated below.

    public interface Predicate<T> {
        public boolean evaluate(T t);
    }
    
    public interface Closure<T> {
        public void execute(T t);
    }

Unfortunately I needed to conceptualize Prepositions and Subjects as mentioned
previously. As an XPer this is usually where I fire up a test and begin
test first design. As expected it produced rather good results. As a starting
point I used the example presented in the article to illustrate the use of commons-collections
as seen below.

    public void testPreposition() {
        Predicate<Student> isPassing = new Predicate<Student>() {
            public boolean evaluate(Student s) {
                return ((s.getGrade() == Grade.A || s.getGrade() == Grade.B));
            }
        };

        Predicate<Student> isNotAbsent = new Predicate<Student>() {
            public boolean evaluate(Student s) {
                return s.getDaysAbsent() == 0;
            }
        };

        final List<Student> honorRoll = new ArrayList<Student>();
        Closure<Student> addToHonorRoll = new Closure<Student>() {
            public void execute(Student student) {
                honorRoll.add(student);
            }
        };

        Student student = new Student(Grade.D, 0);

        when(student, isNotAbsent, addToHonorRoll);
        assertEquals(1, honorRoll.size());
        assertEquals(0, honorRoll.get(0).getDaysAbsent());
        honorRoll.clear();

        List<Student> students = Arrays.asList(new Student[] {
        new Student(Grade.A, 1), new Student(Grade.B, 0),
        new Student(Grade.C, 0) });

        foreach(students, isPassing, addToHonorRoll);
        assertEquals(2, honorRoll.size());
        assertEquals(Grade.A, honorRoll.get(0).getGrade());
        assertEquals(Grade.B, honorRoll.get(1).getGrade());
        honorRoll.clear();
    }

I came to the conclusion that prepositions should be evaluated against subjects,
predicates and closures as illustrated by the following code fragment.

when(student, isNotAbsent, addToHonorRoll);

The preposition would evaluate the subject(student) against the predicate(
isNotAbsent) and execute closure(addToHonorRoll) accordingly. My first attempt
produced Predicate.when(...) however static imports jumped to mind and provided
a rather elegant, scripty, solution. Now I am not a fan of static imports but
this felt like an applicable use. As you will notice I also took advantage of
generics in order to eliminate unneeded casts which in turn creates much more concise
code.

The ability to evaluate Predicate(s) and Closure(s) against a Subject was at hand
however I now needed the ability to evaluate them against multiple Subjects. This
produced the following implementation of Preposition including the features mentioned
above as well.

public class Preposition {
    public static void with(Object subject, Closure c) {
        c.execute(subject);
    }

    public static void when(Object subject, Predicate p, Closure c) {
        if (p.evaluate(subject))
            Preposition.with(subject, c);
    }
    
    public static void foreach(Object[] subjects, Closure c) {
        for (Object subject : subjects) {
            Preposition.with(subject, c);
        }
    }
    
    public static void foreach(Object[] subjects, Predicate p, Closure c) {
        for (Object subject : subjects) {
            Preposition.when(subject, p, c);
        }
    }
    
    public static void foreach(Collection subjects, Closure c) {
        for (Object subject : subjects) {
            Preposition.with(subject, c);
        }
    }
    
    public static void foreach(Collection subjects, Predicate p, Closure c) {
        for (Object subject : subjects) {
            Preposition.when(subject, p, c);
        }
    }
}

I could now use the following syntax to meet this requirement.

foreach(subjects, isPassing, addToHonorRoll);

This was a great start however as I began to port CrazyBob's example I found
something missing. First the inability to execute Closure without a Predicate
as well as the ability to nest Predicates. Fortunately these two problems were
one in the same and were solved by adding the methods above that execute Closure
against a Subject without concern for a Predicate. The above example includes methods
without Predicates but at the time they did not exist.

This refactoring evolved from the following test which is an attempt to get
various methods from a class or interface much like the example in CrazyBob's
article.

    public void testRecursivePreposition() {
        final Predicate<Method> isPublic = new Predicate<Method>() {
            public boolean evaluate(Method m) {
                return Modifier.isPublic(m.getModifiers());
            }
        };

        final Predicate<Method> isProtected = new Predicate<Method>() {
            public boolean evaluate(Method m) {
                return Modifier.isProtected(m.getModifiers());
            }
        };

        final List<Method> methods = new ArrayList<Method>();
        final Closure<Method> addMethod = new Closure<Method>() {
            public void execute(Method m)  {
                    methods.add(m);
            }
        };

        Closure<Class> addMethods = new Closure<Class>() {
            public void execute(Class c)  {
                foreach(c.getDeclaredMethods(), isPublic.or(isProtected), addMethod);
            }
        };

        with(C.class, addMethods);
        assertEquals(4, methods.size());
        assertEquals("getA", methods.get(0).getName());
        assertEquals("getAA", methods.get(1).getName());
        assertEquals("getB", methods.get(2).getName());
        assertEquals("getBB", methods.get(3).getName());
        methods.clear();

        foreach(C.class.getDeclaredMethods(), isProtected, addMethod);
        assertEquals(2, methods.size());
        assertEquals("getAA", methods.get(0).getName());
        assertEquals("getBB", methods.get(1).getName());
        methods.clear();

        foreach(C.class.getInterfaces(), addMethods);
        assertEquals(2, methods.size());
        assertEquals("getA", methods.get(0).getName());
        assertEquals("getB", methods.get(1).getName());
        methods.clear();
    }

    interface A {
    public void getA();
    }

    interface B {
    public void getB();
    }

    class C implements A, B {
        public void getA() { }
        protected void getAA() { }
        private void getAAA() { }

        public void getB() { }
        protected void getBB() { }
        private void getBBB() { }
    }

You will notice that a foreach is nested within the Closure addMethods which
makes reference to the subject passed via the execute method. Additionally
you will notice the introduction of isPublic.or(isProtected). This is a refactoring
of the Predicate class to support conditioning which just shows how these
concepts can be expanded to provide a more robust solution. The code refactorings
follow.

    public abstract class Predicate<T> {
        public abstract boolean evaluate(T t);
    
        public Predicate and(final Predicate p) {
            return Conjunction.and(this, p);
        }
    
        public Predicate or(final Predicate p) {
            return Conjunction.or(this, p);
        }
    }

    public class Conjunction {
        public static Predicate and(final Predicate p1, final Predicate p2) {
            return new Predicate<Object>() {
                public boolean evaluate(Object t) {
                    return p1.evaluate(t) && p2.evaluate(t);
                }
            };
        }

        public static Predicate or(final Predicate p1, final Predicate p2) {
            return new Predicate<Object>() {
                public boolean evaluate(Object t) {
                    return p1.evaluate(t) || p2.evaluate(t);
                }
            };
        }
    }

Obviously this is a rather simplistic example however it illustrates in my opinion very
powerful abstractions and is much cleaner than commons-collection. You
can even implement this yourself without the need for an external library and evolve
it to meet your needs accordingly.

Unfortunately functional objects will never be fully realized until they
are supported implicitly by the language however with some thought and
clever use of 1.5 constructs this is now more a reality than ever before.

On an unrelated note if someone knows why mozilla double spaces the code examples please post a comment.

Related Topics >>