Mapping and Visiting in Functional and Object-Oriented Programming

class Exp{ public abstract T Accept<T,D>(Visitor<T,D> v, D x); } class PlusExp : Exp { private Exp e1, e2; public PlusExp(Exp a, Exp b){ e1 = a; e2 = b; } public Exp LeftOperand{get {return e1;}} public Exp RightOperand{get {return e2;}} public override T Accept<T,D>(Visitor<T,D> v, D x){ return v.Visit(this, x); } } class TimesExp : Exp { private Exp e1, e2; public TimesExp(Exp a, Exp b){ e1 = a; e2 = b; } public Exp LeftOperand{get {return e1;}} public Exp RightOperand{get {return e2;}} public override T Accept<T,D>(Visitor<T,D> v, D x){ return v.Visit(this, x); } } class Identifier : Exp { private string name; public Identifier(string s) {name = s;} public string Name{get {return name;} } public override T Accept<T,D>(Visitor<T,D> v, D x){ return v.Visit(this, x); } } class Literal : Exp { private string litString; public string LiteralString{get {return litString;}} public Literal(string s) {litString = s; } 98 JOURNAL OF OBJECT TECHNOLOGY VOL 7, NO. 7 B THE VISITOR DESIGN PATTERN public override T Accept<T,D>(Visitor<T,D> v, D x){ return v.Visit(this, x); } } class Environment: Dictionary<String, int>{}; class Interpreter: Visitor<int, Environment> { public int Visit(PlusExp e, Environment env){ return (e.LeftOperand.Accept(this, env) + e.RightOperand. Accept(this, env)); } public int Visit(TimesExp e, Environment env){ return (e.LeftOperand.Accept(this, env) * e.RightOperand. Accept(this, env)); } public int Visit(Identifier e, Environment env){ return env[e.Name]; } public int Visit(Literal e, Environment env){ return Int32.Parse(e.LiteralString); } } class ReversePolishConverter: Visitor<string, None> { private const string SPACE = " "; public string Visit(PlusExp e, None x){ return e.LeftOperand. Accept(this, x) + SPACE + e.RightOperand. Accept(this, x) + SPACE + "+"; } public string Visit(TimesExp e, None x){ return e.LeftOperand. Accept(this, x) + SPACE + e.RightOperand. Accept(this, x) + SPACE + "*"; } public string Visit(Identifier e, None x){ return e.Name; } public string Visit(Literal e, None x){ return e.LiteralString; } } class None {}; class Program { VOL 7, NO. 7 JOURNAL OF OBJECT TECHNOLOGY 99 MAPPING AND VISITING IN FUNCTIONAL AND OBJECT-ORIENTED PROGRAMMING public static void Main() { TimesExp ast = new TimesExp(new PlusExp (new Literal("1"), new Identifier("x")), new Literal("2")); // (1 + x) * 2 Environment env = new Environment(); env.Add("x",9); Interpreter interpreter = new Interpreter(); ReversePolishConverter reversPolishConverter = new ReversePolishConverter(); None nothing = new None(); Console.WriteLine(interpreter.Visit(ast, env)); Console.WriteLine(reversPolishConverter.Visit(ast, nothing)); } } } C DOUBLE DISPATCH VISITING IN CLOS ;;; Visitor in CLOS Common Lisp Object System. ;; Expression Classes (defclass Expression () ()) (defclass PlusExpression (Expression) ((a1 :initarg :firstAddend :accessor firstAddend) (a2 :initarg :secondAddend :accessor secondAddend))) (defclass MinusExpression (Expression) ((m :initarg :minuend :accessor minuend) (s :initarg :subtrahend :accessor subtrahend))) (defclass TimesExpression (Expression) ((f1 :initarg :firstFactor :accessor firstFactor) (f2 :initarg :secondFactor :accessor secondFactor))) (defclass DivideExpression (Expression) ((dividend :initarg :dividend :accessor dividend) (divisor :initarg :divisor :accessor divisor))) (defclass Identifier (Expression) ((name :initarg :name :accessor name))) (defclass IntegerLiteral (Expression) ((literal :initarg :literal :accessor literal))) ;; Visitor classes (defclass Visitor () ()) 100 JOURNAL OF OBJECT TECHNOLOGY VOL 7, NO. 7 C DOUBLE DISPATCH VISITING IN CLOS (defclass Interpreter (Visitor) ()) (defclass ReversePolish (Visitor) ()) ;; Visit multi methods. ;; Notice that the visit method specializes on both an Expression ;; and a (kind of) of visitor. (defmethod visit ((e PlusExpression) (v Interpreter) &optional extra) (+ (visit (firstAddend e) v extra) (visit (secondAddend e) v extra))) (defmethod visit ((e MinusExpression) (v Interpreter) &optional extra) ((visit (minuend e) v extra) (visit (subtrahend e) v extra))) (defmethod visit ((e TimesExpression) (v Interpreter) &optional extra) (* (visit (firstFactor e) v extra) (visit (secondFactor e) v extra))) (defmethod visit ((e DivideExpression) (v Interpreter) &optional extra) (/ (visit (dividend e) v extra) (visit (divisor e) v extra))) (defmethod visit ((e Identifier) (v Interpreter) &optional extra) (lookup-identifier extra (name e))) (defmethod visit ((e IntegerLiteral) (v Interpreter) &optional extra) (convert-string-to-integer (literal e))) (defmethod visit ((e PlusExpression) (v ReversePolish) &optional extra) (concatenate ’string (visit (firstAddend e) v extra) " " (visit (SecondAddend e) v extra) " " "+")) (defmethod visit ((e MinusExpression) (v ReversePolish) &optional extra) (concatenate ’string (visit (minuend e) v extra) " " (visit (subtrahend e) v extra) " " "-")) (defmethod visit ((e TimesExpression) (v ReversePolish) &optional extra) (concatenate ’string (visit (firstFactor e) v extra) " " (visit (SecondFactor e) v extra) " " "*")) (defmethod visit ((e DivideExpression) (v ReversePolish) &optional extra) (concatenate ’string (visit (dividend e) v extra) " " (visit (divisor e) v extra) " " "/")) (defmethod visit ((e Identifier) (v ReversePolish) &optional extra) (name e)) (defmethod visit ((e IntegerLiteral) (v ReversePolish) &optional extra) (literal e)) VOL 7, NO. 7 JOURNAL OF OBJECT TECHNOLOGY 101 MAPPING AND VISITING IN FUNCTIONAL AND OBJECT-ORIENTED PROGRAMMING ;; Auxiliary stuff. (defun convert-string-to-integer (str &optional (radix 10)) "Given a digit string and optional radix, return an integer. " ; Details not relevant for this paper ) (defun lookup-identifier (env name) (cdr (assoc name env :test (function equal)))) ;; Some sample visiting: (defun main () (let* ((expr1 (make-instance ’PlusExpression :firstAddend (make-instance ’IntegerLiteral :literal "3") :secondAddend (make-instance ’IntegerLiteral :literal "2"))) (expr2 (make-instance ’TimesExpression :firstFactor (make-instance ’IntegerLiteral :literal "3") :secondFactor (make-instance ’Identifier :name "var"))) (expr3 (make-instance ’MinusExpression :minuend expr1 :subtrahend expr2)) (interpretation (make-instance ’Interpreter)) (reverse-polish (make-instance ’ReversePolish)) (env ’(("x" . 5) ("var" . 7))) ) (list (visit expr1 interpretation env) (visit expr2 interpretation env) (visit expr3 interpretation env) (visit expr1 reverse-polish) (visit expr2 reverse-polish) (visit expr3 reverse-polish) )))