Keywords: current

Next: 1.11 Unusual Situations versus Up: 1.10 The Visitor Pattern Previous: 1.10.3 Polymorphic Visitors

## 1.10.4 Polymorphic Visitors with Arguments

To make our visitor example more interesting and realistic, let us include variables in the type `ArithExpr`.

    class Var extends ArithExpr {
      String name;
      Var(String n) {
        name = n;
      }
      public String toString() {
        return name;
      }
      Object accept(Visitor v) {
        return v.forVar(this);
      }
    }

Then the visitor interface for `ArithExpr`s has the form:

    interface Visitor {
      Object forConst(Const c);
      Object forSum(Sum c);
      Object forProd(Prod p);
      Object forVar(Var v);
    }

The concrete visitor class that implements expression evaluation is:

    class EvalVisitor implements Visitor {
      Env env;  // an environment for looking up variables
      EvalVisitor(Env e) { env = e; }
      Object forConst(Const c) { return c; }
      Object forSum(Sum s) {
        return new Const( ((Const)s.left.accept(this)).value +
                          ((Const)s.right.accept(this)).value );
      }
      Object forProd(Prod p) {
        return new Const( ((Const)p.left.accept(this)).value) *
                          ((Const)p.right.accept(this)).value));
      }
      Object forVar(Var v) { return env.lookup(v.name); }
    }

The environment `env`, which was an explicit parameter of the `eval` method in our method-based implementation for evaluation, is now a field of the visitor. As before, it is directly used only for evaluating instances of `Var`, but now we don't need to explicitly pass the environment through method argument lists.

Since we are programming in a functional style, the `forConst` method need only return its argument as the result, rather than allocating a copy. The `forSum` and `forProd` methods are mostly straightforward, evaluating the subexpressions first and combining the results. The only subtlety is that since `accept` now returns an instance of `Object` rather than an `int`, we need to perform an explicit type cast to get the values for the left and right subexpressions. For example, to obtain the value for the left subexpression in a `Sum`, we have

         ((Const)s.left.accept(this)).value

The expression

> `s.left.accept(this)`

computes a `Const` whose `value` field is the value of the expression `s.left`. But the declared return type for `accept` is `Object`, and since an `Object` has no field named `value`, we cannot extract the value directly. Since we know that the `Object` is in fact a `Const`, we can insert an explicit type cast to `Const`, and then extract the value.

The `forVar` method looks up the value of the variable in the current environment. The environment is passed in when an `EvalVisitor` is created, and is presumably given bindings for the existing variables beforehand.

Finger Exercise Finish the visitor-based implementation of expression evaluation, including the definition of the `Environment` class, by yourself. Can you think of other operations on expressions that the visitor pattern might help you implement?

Next: 1.11 Unusual Situations versus Up: 1.10 The Visitor Pattern Previous: 1.10.3 Polymorphic Visitors