// Now for something Scala does much better than F# ... import exptrees.eval import exptrees.str // create another subclass of Expr anytime you want: case class Divexp(left:Expr,right:Expr) extends Expr object moreexps { // extending the functionality of eval by changing it: var base_eval = eval eval = (e:Expr) => e match { case Divexp(a,b) => eval(a) / eval(b) case x => base_eval(x) }// changed eval definition // This "trick" works because when base_eval calls eval recursively, it will be // the new eval that's being called. Compare this to defining a eval2, which calls // eval as the default case: it would not work because the recursive calls will be made // to eval, not to eval2. But here we are changing eval. This is actually similar to // dynamic scoping, and is considered a controversial way to write functional // programs. We can restore the original meaning of eval with eval = base_eval. var base_str = str str = (e:Expr) => e match { case Divexp(a,b) => s"(${str(a)} / ${str(b)})" case x => base_str(x) } def main(argv:Array[String]):Unit = { val tree = Plusexp(Intexp(20),Uminus(Timesexp(Intexp(4),Intexp(2)))) val tree2 = Timesexp(Intexp(3),Divexp(tree,Intexp(2))) println(str(tree2)) println(eval(tree2)) /* A major advantage that Scala appears to have over ML/F# is that you can define a new case class of an abstract class anytime you want, and pattern matching will still work. This is because, unlike a case in a discriminated union or enum, a case class is just a SUBCLASS. Scala is better integrated with OOP. For the expression tree problem, Scala appears to provide the best solution we've yet seen. But there is always a tradeoff when you have subtyping: wrong type casts may be made that are not caught at compile time, just as we saw in other oop languages. The following code will compile but throws a runtime casting exception if uncommented: */ val e1:Expr = Intexp(2); println( (e1.asInstanceOf[Intexp]).valu.toString ); // val e2:Divexp = e1.asInstanceOf[Divexp]; // compiles, but runtime error // println(str(e2.left)); // It is not possible to even try this in the F# version of expr trees. // The different cases, Intexp, Plusexp, are not subclasses - they're // not even types. }//main }//moreexps // compile together with base program: scalac exptrees.scala moreexps.scala