*=~Visit-o-matic~=*

Reddit delicious.com Digg

Source code on Gitorious
Browse the API
Git changelog
(this is GPL-ed software, distributed without any warranty)

What is it ?

In short

Object-oriented languages such as Java tend to structure the code according to data more than according to functions, attaching the functions (called "methods") to the objects. Functional languages on the other side tend to structure the code according to functions, using pattern matching to dispatch the data.
Visitomatic allows you to structure some parts of your program in the functional way, for when it makes a difference.

An example

For example, let's suppose we have binary trees whose nodes are the "plus" operator and leaves are a "1". The code of a treeSum function for summing the value of those trees would actually be split in many classes when written in Java, as it is in this example :

interface PlusOrJustInteger {
    public int treeSum();
}

class JustInteger implements PlusOrJustInteger {
  private int value = 1;
  …
  public int treeSum() {
    return value;
  }
}

class Plus implements PlusOrJustInteger {
  private PlusOrJustInteger left;
  private PlusOrJustInteger right;
  …
  public int treeSum() {
     return left.treeSum() + right.treeSum();
  }
}

This may, or may not, be the organisation that the user wants : In some cases you want to have all of the treeSum operation in one class.
The classic way of doing this in Object-Oriented programing languages, is the so-called visitor pattern . But in Java, this requires writing boilerplate code which most of the time is not type-safe.

This project allows you to make the design pattern explicit, supressing the need for this tedious code. And it does that without relying on an external pre-processor, using Java annotations instead. Plus it is fast, comes with extensive JavaDoc, is thread-safe, type-safe and hand-made. Love it !

How to use it ?

I will show you how to use VisitOMatic on the treeSum function presented above. I've chosen this example because of its simplicity, but keep in mind that VisitOMatic is more usefull when the visited objects are complex.

Writing our tree

As we said before, an element of this tree is either an Integer or a Plus node with two children. Here is a possible definition for a such tree, please note that it does not specify the implementation of treeSum.

/* An element of the tree */
abstract class PlusOrJustInteger {
}

/* Tree nodes */
class Plus extends PlusOrJustInteger {
    private PlusOrJustInteger left;
    private PlusOrJustInteger right;
}

/* Tree leaves */
class JustInteger extends PlusOrJustInteger
{
    public Integer getValue() {
        return 42;
    }
}

Making it a Visitable

Now we want to mark what is interesting in this tree : in many objects, most fields are not interesting.

/* An element of the tree */
abstract class PlusOrJustInteger {
}

/* Tree nodes, marked as a Visitable */
class Plus extends PlusOrJustInteger implements Visitable {
    @ToVisit // This annotation means that this field is usefull to visitors.
    private PlusOrJustInteger left;
    @ToVisit // Same here.
    private PlusOrJustInteger right;
}

/* Tree leaves */
class JustInteger extends PlusOrJustInteger implements Visitable
{
    @ToVisit // Here the return value is the usefull part
    public Integer getValue() {
        return 42;
    }
}

At this point, our tree is done. We won't modify it when adding the sum operation, or any other operation.

Writing a Visitor, step by step

Now that the tree nodes are Visitable, we now can write our sum and many other functions outside of the tree class. We are helped in doing so by the availability of pattern matching.

Let's start with a SumVisitor class. This class will implement the empty Visitor interface so as to mark it as a visitor.

class SumVisitor implements Visitor {
}

It doesn't do much right now. Let's add some stuff. First we are going to define what to do with objects of class JustInteger. This method will be given the object and its fields annotated with @ToVisit. An annotation identifies it as a part of a visit.

class SumVisitor implements Visitor {
    @VisitingMethod(visitName="sum")
    private int sum(JustInteger it, int value) {
        return value;
    }
}

Let's do the same with the PlusOrJustInteger.

    @VisitingMethod(visitName="sum")
    private int sum(Plus it, PlusOrJustInteger left,
                    PlusOrJustInteger right) {
        return sum(left)+sum(right);
    }

So far so good ? We're quite done. All we need is to add the sum (PlusOrJustInteger tree) method that will call the others.
This require adding an VisitorRunner that is responsible for dispatching the Visitable to the right methods.

    private static final VisitorRunner SUM_RUNNER = VisitorRunner.getInstance(SumVisitor.class, "sum");

And to invoke it when we are given a PlusOrJustInteger :

    public Integer sum(PlusOrJustInteger it) throws VisitorRunnerException {
        return SUM_RUNNER.visit(this, it);
    }

And we're done !

Writing a Visitor, the final result

The final code for the Visitor is :

class SumVisitor implements Visitor {
    /* The VisitorRunner responsible for dispatching the Visitables
     * to the right methods.
     */
    private static final VisitorRunner SUM_RUNNER =
        VisitorRunner.getInstance(SumVisitor.class, "sum");
    
    @VisitingMethod(visitName="sum")
    private int sum(JustInteger it, int value) {
        return value;
    }
    
    @VisitingMethod(visitName="sum")
    private int sum(Plus it, PlusOrJustInteger left,
                    PlusOrJustInteger right) throws VisitorRunnerException {
        return sum(left)+sum(right);
    }
    
    public Integer sum(PlusOrJustInteger it) throws VisitorRunnerException {
        return SUM_RUNNER.visit(this, it);
    }
}

This visitor is a stand-alone implementation of a sum. It can be adapted to other classes and did not require modifying the PlusOrJustInteger class.
This technique is usefull when you want to expose structured data on which API-clients will perform operations, without modifying those data.

This page is standards compliant ! (XHTML 1.0)