There must be some confusion here. Your first paragraph describes what code looks like when you <i>don&#x27;t</i> use the visitor pattern.<p>Just to be explicit, here&#x27;s the kind of code I think you&#x27;re describing (using some vague Java-esque pseudo-code):<p><pre><code>    interface Expression {
      String prettyPrint();
      int evaluate();
    }

    class Plus extends Expression {
      Expression left, right;

      String prettyPrint() {
        return left.prettyPrint() + &quot; + &quot; + right.prettyPrint();
      }

      int evaluate() {
        return left.evaluate() + right.evaluate();
      }
    }

    class Number extends Expression {
      int value;

      String prettyPrint() {
        return value.toString();
      }

      int evaluate() {
        return value;
      }
    }
</code></pre>
This is vanilla OOP code. You have all of the operations on a class directly on that class. Like you note, pretty printing and evaluating are mixed together. With something like an AST, it doesn&#x27;t scale and gets gross.<p>Here&#x27;s the AST classes supporting visitor pattern:<p><pre><code>    interface Expression {
      String prettyPrint();
      int evaluate();

      T accept&lt;T&gt;(Visitor&lt;T&gt; visitor);
    }

    class Plus extends Expression {
      Expression left, right;

      T accept&lt;T&gt;(Visitor&lt;T&gt; visitor) { return visitor.visitPlus(this); }
    }

    class Number extends Expression {
      int value;

      T accept&lt;T&gt;(Visitor&lt;T&gt; visitor) { return visitor.visitNumber(this); }
    }

    interface Visitor&lt;T&gt; {
      T visitPlus(Plus plus);
      T visitNumber(Number number);
    }
</code></pre>
These are now pure data classes, like we want. The only extra bit is the visitor pattern itself.<p>Now we can separate out our concerns. Here&#x27;s pretty-printing:<p><pre><code>    class PrettyPrintVisitor extends Visitor&lt;String&gt; {
      String visitPlus(Plus plus) {
        return plus.left.accept(this) + &quot; + &quot; + plus.right.accept(this);
      }

      String visitNumber(Number number) {
        return number.value.toString();
      }
    }
</code></pre>
And here&#x27;s evaluating:<p><pre><code>    class EvalVisitor extends Visitor&lt;int&gt; {
      int visitPlus(Plus plus) {
        return plus.left.accept(this) + plus.right.accept(this);
      }

      int visitNumber(Number number) {
        return number.value;
      }
    }
</code></pre>
&gt; I&#x27;m going to have a single AST pretty printing function that handles all the cases, a code generating function that handles all the cases and so on.<p>This is exactly what visitor gives you except replace &quot;function&quot; with &quot;class&quot;.<p>&gt; If someone adds a case, they will just have to add them to half a dozen functions.<p>They&#x27;ll have to <i>remember</i> to do that. The visitor pattern makes it a compile error to forget to do that.<p>I also find the pattern scales up better than doing all of the type tests inside a single enormous function. I like having a separate method per AST type.