/**
 * El mismo evaluador en el otro estilo: cada tipo hace override de eval y show.
 * Agregar un tipo es fácil (una clase nueva); agregar una operación obliga a
 * tocar todas las clases.
 */
public class ExprMethods {
    interface Expr {
        int eval();

        String show();
    }

    record Num(int value) implements Expr {
        public int eval() {
            return value;
        }

        public String show() {
            return Integer.toString(value);
        }
    }

    record Add(Expr left, Expr right) implements Expr {
        public int eval() {
            return left.eval() + right.eval();
        }

        public String show() {
            return "(" + left.show() + " + " + right.show() + ")";
        }
    }

    record Mul(Expr left, Expr right) implements Expr {
        public int eval() {
            return left.eval() * right.eval();
        }

        public String show() {
            return left.show() + " * " + right.show();
        }
    }

    public static void main(String[] args) {
        Expr e = new Add(new Num(2), new Mul(new Num(3), new Num(4)));
        IO.println(e.show() + " = " + e.eval());
    }
}
