Skip to content

Instantly share code, notes, and snippets.

@friedbrice
Forked from fiddlerwoaroof/extend.js
Last active June 4, 2019 17:13
Show Gist options
  • Select an option

  • Save friedbrice/915addaf78b562c5038f12e6629c39f1 to your computer and use it in GitHub Desktop.

Select an option

Save friedbrice/915addaf78b562c5038f12e6629c39f1 to your computer and use it in GitHub Desktop.

Revisions

  1. friedbrice revised this gist Jun 4, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Extend.java
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    import java.util.Map;

    // This is almost a solution to the extension problem, except for
    // This is almost a solution to the expression problem, except for
    // lines 47 and 74.
    // Instead of raising an exception, we want the program to fail to
    // compile if there are cases that we haven't considered.
  2. friedbrice revised this gist Jun 4, 2019. 1 changed file with 126 additions and 0 deletions.
    126 changes: 126 additions & 0 deletions Extend.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    import java.util.Map;

    // This is almost a solution to the extension problem, except for
    // lines 47 and 74.
    // Instead of raising an exception, we want the program to fail to
    // compile if there are cases that we haven't considered.
    class Extend {

    // I can define a data structure by cases.

    abstract class Expr {}

    class Constant extends Expr {
    Integer value;
    }

    class Variable extends Expr {
    String name;
    }

    class Add extends Expr {
    Expr left;
    Expr right;
    }

    interface Op<A> {
    A withExpr(Expr x);
    A withConstant(Constant x);
    A withVariable(Variable x);
    A withAdd(Add x);
    }

    // I can add as many new operations as I want.

    class Print implements Op<String> {

    @Override public String withExpr(Expr x) {
    if (x instanceof Constant) return this.withConstant((Constant) x);
    if (x instanceof Variable) return this.withVariable((Variable) x);
    if (x instanceof Add) return this.withAdd((Add) x);
    throw new RuntimeException("This breaks the rules of the game.");
    }

    @Override public String withConstant(Constant x) {
    return x.toString();
    }

    @Override public String withVariable(Variable x) {
    return x.name;
    }

    @Override public String withAdd(Add x) {
    return "(" + this.withExpr(x.left) + " + " + this.withExpr(x.right) + ")";
    }
    }

    class Eval implements Op<Integer> {
    Map<String, Expr> env;

    Eval(Map<String, Expr> env) {
    this.env = env;
    }

    @Override public Integer withExpr(Expr x) {
    if (x instanceof Constant) return this.withConstant((Constant) x);
    if (x instanceof Variable) return this.withVariable((Variable) x);
    if (x instanceof Add) return this.withAdd((Add) x);
    throw new RuntimeException("This breaks the rules of the game.");
    }

    @Override public Integer withConstant(Constant x) {
    return x.value;
    }

    @Override public Integer withVariable(Variable x) {
    if (env.containsKey(x.name)) return this.withExpr(env.get(x.name));
    return new Integer(0);
    }

    @Override public Integer withAdd(Add x) {
    return this.withExpr(x.left) + this.withExpr(x.right);
    }
    }

    // I can add a new case of Expr

    class Mult extends Expr {
    Expr left;
    Expr right;
    }

    interface ExtOp<A> extends Op<A> {
    A withMult(Mult x);
    }

    // And I can update my old operations to work with the new case,
    // without needing access to their source code.

    class ExtPrint extends Print implements ExtOp<String> {

    @Override public String withExpr(Expr x) {
    if (x instanceof Mult) return withMult((Mult) x);
    return super.withExpr(x);
    }

    @Override public String withMult(Mult x) {
    return "(" + this.withExpr(x.left) + " * " + this.withExpr(x.right) + ")";
    }
    }

    class ExtEval extends Eval implements ExtOp<Integer> {

    ExtEval(Map<String, Expr> env) {
    super(env);
    }

    @Override public Integer withExpr(Expr x) {
    if (x instanceof Mult) return withMult((Mult) x);
    return super.withExpr(x);
    }

    @Override public Integer withMult(Mult x) {
    return this.withExpr(x.left) * this.withExpr(x.right);
    }
    }
    }
  3. @fiddlerwoaroof fiddlerwoaroof revised this gist Jun 3, 2019. 1 changed file with 59 additions and 0 deletions.
    59 changes: 59 additions & 0 deletions extend.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,59 @@
    //
    // Two kinds of animals: dogs and cats
    //
    // I want greet and leave to be defined such that there are four
    // different behaviors corresponding to each combination of dog and
    // cat
    //
    class Animal {
    receive_operation(operation) {
    console.log("the animal is unresponsive");
    }
    }

    class Operation {
    operate() {
    console.log("the animal is unresponsive");
    }
    }

    class Turtle extends Animal {}

    class Dog extends Animal {
    receive_operation(operation) {
    // This if statement is hard to add in an extensible fashion
    if (operation instanceof Greet) {
    console.log("the dog wags its tail joyfully");
    } else if (operation instanceof Leave) {
    console.log("the dog looks very sad");
    }
    }
    }

    class Cat extends Animal {
    receive_operation(operation) {
    if (operation instanceof Greet) {
    console.log("the cat stands aloof");
    } else if (operation instanceof Leave) {
    console.log("the cat purs happily");
    }
    }
    }

    class Greet extends Operation {}

    class Leave extends Operation {}

    function operate(animal, operation) {
    animal.receive_operation(operation);
    }

    function main() {
    for (const animal of [new Cat(), new Dog(), new Turtle()]) {
    for (const operation of [new Greet(), new Leave()]) {
    operate(animal, operation);
    }
    }
    }

    main();
  4. @fiddlerwoaroof fiddlerwoaroof revised this gist Jun 3, 2019. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions extend.lisp
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@
    (defclass operation ()
    ())

    (defgeneric visit (animal operation)
    (defgeneric operate (animal operation)
    #| This method ensures type safety: as long as we're given something in the domain of the function
    (animal, operation) => effect}, we'll get a result. |#
    (:method ((animal animal) (operation operation))
    @@ -33,14 +33,14 @@
    (defclass leave (operation)
    ())

    (defmethod visit ((animal cat) (operation greet))
    (defmethod operate ((animal cat) (operation greet))
    (format *standard-output* "the cat stands aloof~%"))
    (defmethod visit ((animal cat) (operation leave))
    (defmethod operate ((animal cat) (operation leave))
    (format *standard-output* "the cat purs happily~%"))

    (defmethod visit ((animal dog) (operation greet))
    (defmethod operate ((animal dog) (operation greet))
    (format *standard-output* "the dog wags its tail joyfully~%"))
    (defmethod visit ((animal dog) (operation leave))
    (defmethod operate ((animal dog) (operation leave))
    (format *standard-output* "the dog looks very sad~%"))

    (defun main ()
    @@ -58,4 +58,4 @@ the dog wags its tail joyfully
    the dog looks very sad
    the animal is unresponsive
    the animal is unresponsive
    |#
    |#
  5. @fiddlerwoaroof fiddlerwoaroof revised this gist Jun 3, 2019. 1 changed file with 28 additions and 18 deletions.
    46 changes: 28 additions & 18 deletions extend.lisp
    Original file line number Diff line number Diff line change
    @@ -8,6 +8,15 @@
    (defclass animal ()
    ())

    (defclass operation ()
    ())

    (defgeneric visit (animal operation)
    #| This method ensures type safety: as long as we're given something in the domain of the function
    (animal, operation) => effect}, we'll get a result. |#
    (:method ((animal animal) (operation operation))
    (format *standard-output* "the animal is unresponsive~%")))

    (defclass turtle (animal)
    ())
    #| -------------- End Library -------------- |#
    @@ -18,28 +27,29 @@
    (defclass dog (animal)
    (#| dog-specific members here |#))

    (defgeneric greet (thing)
    (:method ((thing animal))
    (format *standard-output* "the animal is unresponsive~%"))
    (:method ((thing dog))
    (format *standard-output* "the dog wags its tail joyfully~%"))
    (:method ((thing cat))
    (format *standard-output* "the cat stands aloof~%")))

    (defgeneric leave (thing)
    (:method ((thing animal))
    (format *standard-output* "the animal is unresponsive~%"))
    (:method ((thing dog))
    (format *standard-output* "the dog looks very sad~%"))
    (:method ((thing cat))
    (format *standard-output* "the cat purs happily~%")))
    (defclass greet (operation)
    ())

    (defclass leave (operation)
    ())

    (defmethod visit ((animal cat) (operation greet))
    (format *standard-output* "the cat stands aloof~%"))
    (defmethod visit ((animal cat) (operation leave))
    (format *standard-output* "the cat purs happily~%"))

    (defmethod visit ((animal dog) (operation greet))
    (format *standard-output* "the dog wags its tail joyfully~%"))
    (defmethod visit ((animal dog) (operation leave))
    (format *standard-output* "the dog looks very sad~%"))

    (defun main ()
    (dolist (animal (list (make-instance 'cat)
    (make-instance 'dog)
    (make-instance 'turtle)))
    (dolist (action (list 'greet 'leave))
    (funcall action animal)))) #| ===>
    (dolist (action (list (make-instance 'greet)
    (make-instance 'leave)))
    (visit animal action)))) #| ===>
    FWOAR.EXTEND> (main)
    the cat stands aloof
    @@ -48,4 +58,4 @@ the dog wags its tail joyfully
    the dog looks very sad
    the animal is unresponsive
    the animal is unresponsive
    |#
    |#
  6. @fiddlerwoaroof fiddlerwoaroof created this gist Jun 3, 2019.
    51 changes: 51 additions & 0 deletions extend.lisp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,51 @@
    (defpackage :fwoar.extend
    (:use :cl )
    (:export ))

    (in-package :fwoar.extend)

    #| ------------- Begin Library ------------- |#
    (defclass animal ()
    ())

    (defclass turtle (animal)
    ())
    #| -------------- End Library -------------- |#

    (defclass cat (animal)
    (#| cat-specific members here |#))

    (defclass dog (animal)
    (#| dog-specific members here |#))

    (defgeneric greet (thing)
    (:method ((thing animal))
    (format *standard-output* "the animal is unresponsive~%"))
    (:method ((thing dog))
    (format *standard-output* "the dog wags its tail joyfully~%"))
    (:method ((thing cat))
    (format *standard-output* "the cat stands aloof~%")))

    (defgeneric leave (thing)
    (:method ((thing animal))
    (format *standard-output* "the animal is unresponsive~%"))
    (:method ((thing dog))
    (format *standard-output* "the dog looks very sad~%"))
    (:method ((thing cat))
    (format *standard-output* "the cat purs happily~%")))

    (defun main ()
    (dolist (animal (list (make-instance 'cat)
    (make-instance 'dog)
    (make-instance 'turtle)))
    (dolist (action (list 'greet 'leave))
    (funcall action animal)))) #| ===>
    FWOAR.EXTEND> (main)
    the cat stands aloof
    the cat purs happily
    the dog wags its tail joyfully
    the dog looks very sad
    the animal is unresponsive
    the animal is unresponsive
    |#