MCS-287 Notes for 2006-02-28

The visitor classes we have seen (such as Evaluator and Converter) have methods with different names: visitSum, which takes an argument that is a Sum, visitProduct, which takes an argument that is a Product, and visitConstant, which takes a Constant. Would it be possible to shorten the name of all three methods to just visit? For example, could the three methods in the Evaluator class be defined as shown below, provided that we correspondingly changed the places where they are called?

    public Integer visit(Sum s){
        return s.getLeft().accept(this) + s.getRight().accept(this);
    }

    public Integer visit(Product p){
        return p.getLeft().accept(this) * p.getRight().accept(this);
    }

    public Integer visit(Constant c){
        return c.getValue();
    }

The answer to this question is "yes". In Java, as well as many other languages, a class can have multiple methods that have the same name, so long as the argument types suffice to distinguish them. This feature is known as overloading. Inside the Sum class's accept method, where this is known to be of type Sum, the method invocation v.visit(this) can unambiguously be understood as invoking the visit method that has a Sum argument, just as much so as when that method was named visitSum.

I usually prefer not to use overloading because it often detracts from clarity and because it can make bugs harder to track down. However, even if you never use it, you need to understand it. It also leads naturally into our next topic, multimethods.

Supposing you rename all the visiting methods to visit, as shown above, it is tempting to think that the accept methods in the AST classes become unnecessary. For example, couldn't we rewrite the Evaluator class's method for visiting a Sum as follows?

    public Integer visit(Sum s){
        return this.visit(s.getLeft()) + this.visit(s.getRight());
    }

This code looks right, but it won't work. In fact, it will cause a compile-time error, because there is no visit method that takes an argument of type Expr, which is the type (from the compiler's static perspective) of s.getLeft() and s.getRight(). The fact that each runtime object that is a left or right operand will always in fact be either a Sum, a Product, or a Constant doesn't help any, even though there are visit methods accepting each of these. The compile-time error can be made to go away by adding one more method to the Evaluator class:

    public Integer visit(Expr e){
        throw new Error("Unknown kind of Expr: " + e);
    }

However, if you try running this program, you will find that the error message is always produced. The compiler selects the appropriate one of the overloaded visit methods by using only the static type of the argument. The dynamic type of object passed at runtime has no influence, unlike the type of object before the dot. We say that Java uses single dispatch: only the type of one object (the one before the dot) influences the runtime method dispatching. This explains why the visitor pattern needs to use both the accept methods in the AST classes and the visit methods in the visitor classes. By using single dispatching twice (known as double dispatching), both the type of the AST node object and the type of the visitor can influence the code that runs.

If we are willing to go outside of Java and look at other programming languages, then something like the simplified version can work, and we won't need the accept methods. For example, in the MultiJava language (which is an extension of Java), one could write the methods in Evaluator to look like this:

    public Integer visit(Expr@Sum s){
        return this.visit(s.getLeft()) + this.visit(s.getRight());
    }

This visit method applies in situations where the argument's static type is Expr, but where the dynamic type of the actual object passed at runtime turns out to be Sum. This feature is known as multiple dispatch. Methods that use multiple dispatch are known as multimethods. In a language with multimethods, you can declare a visit method that runs when the object doing the visiting happens to be an Evaluator and the object being visited happens to be a Sum. The above MultiJava method would do this, assuming it appears within the Evaluator class.


Course web site: http://www.gustavus.edu/+max/courses/S2006/MCS-287/
Instructor: Max Hailperin <max@gustavus.edu>