// requires java21 import java.util.Optional; interface expr { default int evalmethod() { return expr24.eval(this); } default Optional eval() { return switch (this) { case Int(int x) -> Optional.of(x); case Plus(expr a, expr b) -> a.eval().flatMap(x -> b.eval().map(y -> x+y)); case Times(expr a, expr b) -> a.eval().flatMap(x -> b.eval().map(y -> x*y)); case Neg(expr a) -> a.eval().map(x -> -1 * x); case Div(expr a, expr b) -> b.eval().flatMap(y -> { if (y!=0) return a.eval().map(x -> x/y); else return Optional.empty(); }); default -> Optional.empty(); }; } // eval as method default String to_string() { // can't override toString in interface switch (this) { case Int(int x): return x+""; case Plus(expr a, Neg(expr b)): return "("+a.to_string() + " - " + b.to_string() + ")"; case Plus(expr a, expr b): return "("+a.to_string() + " + " + b.to_string() + ")"; case Times(expr a, expr b): return a.to_string() + " * " + b.to_string(); case Div(expr a, Int(int x)) when x==1 : return a.to_string(); case Div(expr a, expr b): return a.to_string() + " / " + b.to_string(); case Neg(Neg(expr a)): return a.to_string(); case Neg(expr a): return "-"+a.to_string(); default: return ""; } } }//interface - looking more like a Rust trait... record Int(int x) implements expr {} record Plus(expr a, expr b) implements expr {} record Times(expr a, expr b) implements expr {} record Div(expr a, expr b) implements expr {} record Neg(expr a) implements expr {} public class expr24 { static int safediv(int a, int b) { if (b!=0) return a/b; else throw new RuntimeException("div by zero"); } static int eval(expr expression) { return switch (expression) { case null -> throw new RuntimeException("it's Hoare's fault!"); case Int(int x) -> x; case Plus(expr a, expr b) -> eval(a) + eval(b); case Times(expr c, expr d) -> eval(c) * eval(d); case Neg(Neg(expr a)) -> eval(a); // nested pattern match case Neg e -> eval(e.a()) * -1; case Div(expr a, expr b) -> safediv(eval(a), eval(b)); /* var d = eval(b); if (d!=0) yield eval(a)/d; else throw new RuntimeException("div by zero"); */ default -> throw new RuntimeException("unknown expr type"); // default does not cover null pointer case }; }//eval // eval with monadic error handling instead of exception throwing static Optional try_eval(expr expression) { return switch (expression) { case null -> Optional.empty(); case Int(int x) -> Optional.of(x); case Plus(expr a, expr b) -> try_eval(a).flatMap(x -> try_eval(b).map(y -> x+y)); case Times(expr a, expr b) -> try_eval(a).flatMap(x -> try_eval(b).map(y -> x*y)); case Neg(expr a) -> try_eval(a).map(x -> -1*x); case Div(expr a, expr b) -> try_eval(b).flatMap(y -> { if (y!=0) return try_eval(a).map(x -> x/y); else return Optional.empty(); }); default -> Optional.empty(); }; } // try_eval public static void main(String[] args) { expr e = new Plus(new Int(3), new Neg(new Times(new Int(5),new Neg(new Int(7))))); System.out.printf("evals to %d\n",eval(e)); System.out.printf("method evals to %d\n",e.evalmethod()); expr e2 = null; System.out.println("e2 evals to " + try_eval(e2)); e.eval() .ifPresent(r -> System.out.printf("%s evals to %d\n",e.to_string(),r)); }//main } record tuple(A car, B cdr) {};