Dynamic visitor builder with closures

5

In my previous post I was fumbling with how to leverage closures to improve the visitor pattern. Neal suggested that I could leverage closures in building the visitor rather than executing it.

So, here’s a hack at doing that. The Visitor part is all standard. [I'm taking the easy route of encoding navigation in the data structure and yes I know there are better ways but this is easier to show.]

public interface Visitable { void accept(Visitor visitor); }
public interface Node extends Visitable {}

public class ConcreteNode implements Node {
public void accept(Visitor visitor) { visitor.visit(this); }
}

public class CompositeNode implements Node {
public void accept(Visitor visitor) {
visitor.visit(this);
for(Node node : nodes) {
node.accept(visitor);
}
}
}

public interface Visitor {
void visit(ConcreteNode node);
void visit(CompositeNode node);
}

We then can create a VisitorBuilder which is just a builder to create DynamicVisitor instances:

public class VisitorBuilder {
public static VisitorBuilder visitor() {
return new VisitorBuilder();
}

private DynamicVisitor visitor = new DynamicVisitor();

public VisitorBuilder handleConcreteNode({ConcreteNode=>void} block) {
visitor.addConcreteNode(block);
return this;
}

public VisitorBuilder handleCompositeNode({CompositeNode=>void} block) {
visitor.addCompositeNode(block);
return this;
}

public Visitor build() {
return this.visitor;
}
}

public class DynamicVisitor implements Visitor {
private {ConcreteNode=>void} concreteNodeBlock;
private {CompositeNode=>void} compositeNodeBlock;

public void addConcreteNode({ConcreteNode=>void} block) {
this.concreteNodeBlock = block;
}

public void addCompositeNode({CompositeNode=>void} block) {
this.compositeNodeBlock = block;
}

public void visit(ConcreteNode node) {
if(concreteNodeBlock != null) {
concreteNodeBlock.invoke(node);
}
}

public void visit(CompositeNode node) {
if(compositeNodeBlock != null) {
compositeNodeBlock.invoke(node);
}
}
}

You then can dynamically assemble a visitor from blocks by doing something like:

Visitor visitor =
VisitorBuilder.visitor()
.handleConcreteNode({ConcreteNode node=>
System.out.println(“Visiting ConcreteNode”);
})
.handleCompositeNode({CompositeNode node=>
System.out.println(“Visiting CompositeNode”);
})
.build();

Node root = …
root.acceptVisitor(visitor);

You’ll note that this kind of sucks in the naming of the handle and add methods. Due to type erasure, all of the blocks passed to these methods have the same type signature so I can’t overload handle() and add() methods. I also don’t know of any reflective interface on the block that would let me use reflection to discover the parameter type of the closure (also missing due to erasure I would think).

One alternative would be to have a single handle() method on the builder that took a class as an extra parameter and then encoded a big if/else for all possible types within the handle method. In the fluent style you could then have something like:

Visitor visitor =
VisitorBuilder.visitor()
.on(ConcreteNode.class).do({ConcreteNode node=>
System.out.println(“Visiting ConcreteNode”);
})
.on(CompositeNode.class).do({CompositeNode node=>
System.out.println(“Visiting CompositeNode”);
})
.build();

There are probably some interesting extensions to this that are model-specific. So, if you had many nodes that shared some common attribute, you could have methods on the builder that could apply the closure to a set of node types in one call. That sort of thing could let you build some kinds of visitors in a lot less code.

Another nice consequence is that since the closures can reach the local state during build time, it is easy to have the closures update local variables, such as by adding to a collection. I believe there are also some nice improvements for the cases of return values and exception handling. I’ll play with those next.

Comments

5 Responses to “Dynamic visitor builder with closures”
  1. Andrew says:

    Ok, so it’s assumed that I’ll chide you about how you’ve implemented your visitor. This is because you haven’t implemented the Visitor pattern but some kind of iterator.

    By allowing the visitor to control the order of visiting children (ie the visitor ‘wears the pants’) the flexibility is increased dramatically.

    But let’s put that aside for the moment (but it will be revisited).

    Your first proposal here (ie handleConcreteNode) I didn’t find particularly convincing. For a pattern that’s synonymous with boring boilerplate code, you’ve managed to kick it up another notch. For all that extra work to set up the visitor, I could see everybody but the most die-hard closure fan doing their Visitor the regular way.

    Your alternate proposal was a bit more interesting – not so much because of the involvement of closures, but because it’s the first steps down emulating dynamic dispatch. So instead of the big if/then statement (which would effectively be the existing static dispatch code pivoted into a single dispatch), you just tested each object against the list of handlers (isInstance). At least that way you can magically avoid all that extra boilerplate.

    My main problem is resuscitating the actual Visitor pattern.

    Normally I might do something like this:

    class ChattyVisitor extends TreeVisitor {
    void visitFoo(FooNode node) {
    System.out.println(“Let’s visit foo’s children”);
    super.visitFoo(node);
    System.out.println(“That was fun!”);
    }
    }

    where TreeVisitor is the implementation of Visitor that provides a default visitation pattern.

    The key line there is “super.visitFoo(node);”. Depending on which closure proposal you run with, you may or not be able to say that – so it’s just a case of coming up with a suitable analog.

    At the end of the day, it comes down to whether your visitors can do interesting things (e.g. XPath interpreter), or only really banal operations (e.g. count number of certain type of node).

  2. Alex says:

    Normally I would never do iteration in the data structure itself. I would usually pull it out into a separate navigation visitor. I don’t like subclassing the navigation visitor as I generally dislike inheritance where composition will do. Generally I like the navigation visitor to take a logic visitor along for the ride.

    But that to me is really orthogonal to what I’m trying to understand (which is why I skipped the longer code to do it). I think the closure version has some interesting possible solutions for common problems I see with the normal visitor pattern.

    One common visitor type is a “collector” visitor that picks up state as it walks through the data structure. Most commonly, that state is held in the visitor. But with a closure-based form, you can store the state in the calling method’s data structure. Not a big thing, but interesting.

    Another common visitor type is a “finder” that is searching for something and then needs to abort early. That’s a bit more annoying to implement (not hard, just annoying). With unrestricted closures, you can actual return directly from the calling block, which is pretty cool.

    I haven’t really looked at the exception handling aspect, but I suspect with BGGA at least the exception handling aspects would let you do something better than your typical non-closure visitor.

    This is a thought experiment, not a thesis statement. I’m just interested to see where this goes and whether it opens any useful doors.

  3. Andrew says:

    Sorry for coming across as too aggressive.

    I think the path you are walking is interesting – my warnings are merely to ensure that you cover the interesting cases and/or consider where closures are actually helping.

    For example, in your alternative implementation, is that so much better syntax-wise than an anonymous inner class?

    ie something like:
    # TreeVisitor visitor = new TreeVisitor() {
    # @Override
    # void visitConcreteNode(ConcreteNode node) {
    # System.out.println(“Visiting ConcreteNode”);
    # }
    # @Override
    # void visitCompositeNode(CompositeNode node) {
    # System.out.println(“Visiting CompositeNode”);
    # }
    # }

    Of course the problem with anon inner classes, is that while the object forms a simple closure, it’s less than useful because it’s so hard to get the info out – thus the immense handiness of the C# “var” keyword.

    Which brings me to my main complaint of the closure proposals – they seem to be primarily concerned with (and complicated by) implementing first class functions – where if they merely concerned themselves with closure support for anonymous inner classes, people would find them far less confusing.

    ie closures don’t actually require functions to be useful.

  4. steve says:

    A great way to find good design/coding patterns for closures is to check out the other languages that have had them for a while. Lots of cool stuff in Smalltalk for instance.

  5. Mikko says:

    The type parameters of closures can be obtained very easily via reflection on the invoke method.
    A more detailed description is available here.

Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!