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
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save friedbrice/915addaf78b562c5038f12e6629c39f1 to your computer and use it in GitHub Desktop.
Save friedbrice/915addaf78b562c5038f12e6629c39f1 to your computer and use it in GitHub Desktop.
Static typable EP solution
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);
}
}
}
//
// 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();
(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