package expAbstractOperation; /** A base class consisting of * - a root trait (gi.e. abstract class) `Exp' with an `accept' function * to apply an arbitrary visitor. * - a concrete instance class `Num' of `Exp' for numeric literals * - a root trait `Visitor' for visitors. * - an abstract type `visitor' bounded by `Visitor' * - a concrete instance `Eval' of visitors for evaluation. */ trait Base { trait Exp { def accept(v: visitor): unit } case class Num(value: int) extends Exp { def accept(v: visitor): unit = v.caseNum(value); } type visitor <: Visitor; /** Visitor methods always return `unit'. If a visitor conceptually * returns a result, this must be stored in the visitor itself. * Note that the `Visitor' type cannot be parameterized, since * the abstract type `visitor' is bound by it. If we had constructor * polymorphism in Scala, we could also represent visitors that * are parameterized with their result type. */ trait Visitor { def caseNum(value: int): unit; } /** The concrete visitor class `Eval'. Besides the `visit' method, * it contains * - a variable `result' to receive the result of the visiting methods. * - a method `apply' which applies the visitor to a given expression * tree and returns the result stored in the visitor. * Note the explicit self type `visitor' for class `Eval'. It gives `this' * in `Eval' the type `visitor'. This is necessary for formulating the * `apply' method in a type-safe way, and also allows the recursive * application of the visitor in the extensions below. */ trait Eval requires (visitor with Eval) extends Visitor { var result: int = _; def apply(t: Exp): int = { t.accept(this); result } def caseNum(value: int): unit = result = value; } } /** Data extension 1: An extension of `Base' with `Plus' expressions * It contains a new case class for `PLus' expressions, as well as: * - A refinement of the `Visitor' class to handle `Plus' expressions. * - A re-binding of the abstract type `visitor' to the new Visitor class. * - A refinement of the `Eval' class to handle `Plus' expressions. */ trait BasePlus extends Base { type visitor <: Visitor; trait Visitor extends super.Visitor { def casePlus(left: Exp, right: Exp): unit; } case class Plus(left: Exp, right: Exp) extends Exp { def accept(v: visitor): unit = v.casePlus(left, right); } trait Eval requires (visitor with Eval) extends super.Eval with Visitor { def casePlus(left: Exp, right: Exp): unit = result = apply(left) + apply(right); } } /** A test object for the combination class. * It ``ties the knot'' by equating the abstract type `visitor' with * the class `Visitor'. */ object testBasePlus extends BasePlus with Application { type visitor = Visitor; val term = new Plus(new Num(1), new Num(2)); System.out.println(new Eval {} apply term) } /////////////////////////////////////////////////////////////////// /** Data extension 2: An extension of `Base' with `Neg' expressions */ trait BaseNeg extends Base { type visitor <: Visitor; trait Visitor extends super.Visitor { def caseNeg(term: Exp): unit; } case class Neg(term: Exp) extends Exp { def accept(v: visitor): unit = v.caseNeg(term); } trait Eval requires (visitor with Eval) extends super.Eval with Visitor { def caseNeg(term: Exp): unit = result = - apply(term); } } /** Combining both data extensions with a deep mixin composition. */ trait BasePlusNeg extends BasePlus with BaseNeg { type visitor <: Visitor; trait Visitor extends super[BasePlus].Visitor with super[BaseNeg].Visitor; trait Eval requires (visitor with Eval) extends super[BasePlus].Eval with super[BaseNeg].Eval with Visitor; } /** Testing the combination */ object testBasePlusNeg extends BasePlusNeg with Application { type visitor = Visitor; val term = Neg(Plus(Num(1), Num(2))); System.out.println(new Eval {} apply term) } /////////////////////////////////////////////////////////////////////////// /** Operation extension 1: Adding a show visitor */ trait ShowPlusNeg extends BasePlusNeg { trait Show requires (visitor with Show) extends Visitor { var result: String = _; def apply(t: Exp): String = { t.accept(this); result } def caseNum(value: int): unit = result = value.toString(); def casePlus(left: Exp, right: Exp): unit = result = apply(left) + "+" + apply(right); def caseNeg(term: Exp): unit = result = "-(" + apply(term) + ")"; } } /** Operation extension 2: An extension of `BasePlusNeg' with a `dble' method, * which returns an expression tree representing the original tree times two. */ trait DblePlusNeg extends BasePlusNeg { trait Dble requires (visitor with Dble) extends Visitor { var result: Exp = _; def apply(t: Exp): Exp = { t.accept(this); result } def caseNum(value: int): unit = result = Num(2 * value); def casePlus(left: Exp, right: Exp): unit = result = Plus(apply(left), apply(right)); def caseNeg(term: Exp): unit = result = Neg(apply(term)); } } /** Combining the two operation extensions with a shallow mixin composition. */ trait ShowDblePlusNeg extends DblePlusNeg with ShowPlusNeg; /** Testing the resulting combination. */ object testShowDblePlusNeg extends ShowDblePlusNeg with Application { type visitor = Visitor; val term = Neg(Plus(Num(1), Num(3))); System.out.println("2 * (" + (new Show {} apply term) + ") = " + (new Eval {} apply (new Dble {} apply term))); }