-
-
Save friedbrice/915addaf78b562c5038f12e6629c39f1 to your computer and use it in GitHub Desktop.
Static typable EP solution
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Map; | |
// 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. | |
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defpackage :fwoar.extend | |
(:use :cl ) | |
(:export )) | |
(in-package :fwoar.extend) | |
#| ------------- Begin Library ------------- |# | |
(defclass animal () | |
()) | |
(defclass 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)) | |
(format *standard-output* "the animal is unresponsive~%"))) | |
(defclass turtle (animal) | |
()) | |
#| -------------- End Library -------------- |# | |
(defclass cat (animal) | |
(#| cat-specific members here |#)) | |
(defclass dog (animal) | |
(#| dog-specific members here |#)) | |
(defclass greet (operation) | |
()) | |
(defclass leave (operation) | |
()) | |
(defmethod operate ((animal cat) (operation greet)) | |
(format *standard-output* "the cat stands aloof~%")) | |
(defmethod operate ((animal cat) (operation leave)) | |
(format *standard-output* "the cat purs happily~%")) | |
(defmethod operate ((animal dog) (operation greet)) | |
(format *standard-output* "the dog wags its tail joyfully~%")) | |
(defmethod operate ((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 (make-instance 'greet) | |
(make-instance 'leave))) | |
(visit animal action)))) #| ===> | |
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 | |
|# |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment