// requires java21 import java.util.Optional; 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 {} sealed interface expr permits Int, Plus, Times, Div, Neg { 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(); }); }; } // eval 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(); } }// to_string }//interface - looking more like a Rust trait... public class exprnew { 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))))); e.eval() .ifPresent(r -> System.out.printf("%s evals to %d\n",e.to_string(),r)); }//main }