content: package lambdasinaction.chap14;
  
  import java.util.function.Function;
  import java.util.function.Supplier;
  
  public class PatternMatching {
  
      public static void main(String[] args) {
          simplify();
  
          Expr e = new BinOp("+", new Number(5), new BinOp("*", new Number(3), new Number(4)));
          Integer result = evaluate(e);
          System.out.println(e + " = " + result);
      }
  
      private static void simplify() {
          TriFunction<String, Expr, Expr, Expr> binopcase =
                  (opname, left, right) -> {
                      if ("+".equals(opname)) {
                          if (left instanceof Number && ((Number) left).val == 0) {
                              return right;
                          }
                          if (right instanceof Number && ((Number) right).val == 0) {
                              return left;
                          }
                      }
                      if ("*".equals(opname)) {
                          if (left instanceof Number && ((Number) left).val == 1) {
                              return right;
                          }
                          if (right instanceof Number && ((Number) right).val == 1) {
                              return left;
                          }
                      }
                      return new BinOp(opname, left, right);
                  };
          Function<Integer, Expr> numcase = val -> new Number(val);
          Supplier<Expr> defaultcase = () -> new Number(0);
  
          Expr e = new BinOp("+", new Number(5), new Number(0));
          Expr match = patternMatchExpr(e, binopcase, numcase, defaultcase);
          if (match instanceof Number) {
              System.out.println("Number: " + match);
          } else if (match instanceof BinOp) {
              System.out.println("BinOp: " + match);
          }
      }
  
      private static Integer evaluate(Expr e) {
          Function<Integer, Integer> numcase = val -> val;
          Supplier<Integer> defaultcase = () -> 0;
          TriFunction<String, Expr, Expr, Integer> binopcase =
                  (opname, left, right) -> {
                      if ("+".equals(opname)) {
                          if (left instanceof Number && right instanceof Number) {
                              return ((Number) left).val + ((Number) right).val;
                          }
                          if (right instanceof Number && left instanceof BinOp) {
                              return ((Number) right).val + evaluate((BinOp) left);
                          }
                          if (left instanceof Number && right instanceof BinOp) {
                              return ((Number) left).val + evaluate((BinOp) right);
                          }
                          if (left instanceof BinOp && right instanceof BinOp) {
                              return evaluate((BinOp) left) + evaluate((BinOp) right);
                          }
                      }
                      if ("*".equals(opname)) {
                          if (left instanceof Number && right instanceof Number) {
                              return ((Number) left).val * ((Number) right).val;
                          }
                          if (right instanceof Number && left instanceof BinOp) {
                              return ((Number) right).val * evaluate((BinOp) left);
                          }
                          if (left instanceof Number && right instanceof BinOp) {
                              return ((Number) left).val * evaluate((BinOp) right);
                          }
                          if (left instanceof BinOp && right instanceof BinOp) {
                              return evaluate((BinOp) left) * evaluate((BinOp) right);
                          }
                      }
                      return defaultcase.get();
                  };
  
          return patternMatchExpr(e, binopcase, numcase, defaultcase);
      }
  
      static class Expr {
      }
  
      static class Number extends Expr {
          int val;
          public Number(int val) {
              this.val = val;
          }
  
          @Override
          public String toString() {
              return "" + val;
          }
      }
  
      static class BinOp extends Expr {
          String opname;
          Expr left, right;
          public BinOp(String opname, Expr left, Expr right) {
              this.opname = opname;
              this.left = left;
              this.right = right;
          }
  
          @Override
          public String toString() {
              return "(" + left + " " + opname + " " + right + ")";
          }
      }
  
      static <T> T MyIf(boolean b, Supplier<T> truecase, Supplier<T> falsecase) {
          return b ? truecase.get() : falsecase.get();
      }
  
      static interface TriFunction<S, T, U, R> {
          R apply(S s, T t, U u);
      }
  
      static <T> T patternMatchExpr(Expr e,
              TriFunction<String, Expr, Expr, T> binopcase,
              Function<Integer, T> numcase, Supplier<T> defaultcase) {
  
          if (e instanceof BinOp) {
              return binopcase.apply(((BinOp) e).opname, ((BinOp) e).left, ((BinOp) e).right);
          } else if (e instanceof Number) {
              return numcase.apply(((Number) e).val);
          } else {
              return defaultcase.get();
          }
      }
  
  }
  