Visitors Galore

My Advisor, Dr. Mayer Goldberg, claims that object-oriented programming is like functional programming, only with named arguments. Objects are closures. Constructors are lambdas. Invoking a method is applying the closure. That’s it.

I faced a good demonstrations of that claim while I was working on a small code generator. I wanted to exploit Java’s annotation processing, so I can make this generator a part of a build process. The annotation processing mechanism uses the visitor design pattern extensively. By “extensively” I actually mean “you cannot get a concrete value without using at least two visitors”. These visitors are:

  1. Element visitor: Since Java code is composed of packages, classes, methods and so on, you need a visitor to tell you which kind of element you are currenly holding.
  2. Type visitors: A type in Java can be a declared type (class, interface, etc.), an array type, primitive type and so forth, so you need a visitor to tell you which type is which.

The task that I had at hand looked simple: Given a class with methods that has a parameteric return type, get the type argument that is not the class. For example, for this class and method:

public class A {
  Association<A,B> getAB();
}

Return the element representing the type “B”. Simple enough, no?

That simple task required no less then 4 levels of nested visitors, shown here with some omissions. The first element is the class “A”.

tElem.accept(new SimpleElementVisitor6<Void, ...>() {
  public Void visitType(TypeElement e, ...) {
    for (Element tSubElem : e.getEnclosedElements()) {
      tSubElem.accept(new SimpleElementVisitor6<AssocEndSpec, ...>() {
        public AssocEndSpec visitExecutable(ExecutableElement ex, ...) {
          TypeMirror tRetTypeMirror = ex.getReturnType();
          tRetTypeMirror.accept(new SimpleTypeVisitor6<TypeElement, TypeElement>() {
            public TypeElement visitDeclared(DeclaredType t, TypeElement enclose) {
              for (TypeMirror tTypeArgMirror : t.getTypeArguments()) {
                tTypeArgMirror.accept(new SimpleTypeVisitor6<TypeElement, ...>() {
                  public TypeElement visitDeclared(DeclaredType t, TypeElement self) {
                    TypeElement tArgTypeElem = (TypeElement) t.asElement();
                    if (!self.equals(tArgTypeElem)) {
                      // found the little bugger!
                    }
                  }
                }, ...);
              }
            }
          }, ...);
        }
    }, ...);
  }
}, ...);

That looks more like a piece of ML of Scheme code then Java! Of course I refactored it later, but it doesn’t change the simple fact that it takes 4 levels of double-dispatch just to get that little type argument. That more type-safe and well-structured then a perl hack script that would do just that, but matching the method declaration line with the following regular expression is a bit more brief:

/\w+<(\w+),\s*(\w+)>/

Btw, those of sharp sight will notice that I skipped one more visitor by using casting. I appreciate type-safety like any other programmer, but enough is enough!

Posted Sunday, January 11th, 2009 under Programming.

Tags: , , ,

Leave a Reply

Security Code: