expression problem

26
The Expression Problem Attila Magyar, 2016 Microsec ltd.

Upload: attila-magyar

Post on 16-Mar-2018

90 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Expression problem

The Expression Problem

Attila Magyar, 2016Microsec ltd.

Page 2: Expression problem

Example● Types ● Operations

● Area● Perimeter● ..●

Area Perimeter ??

Square OK OK

Circle OK Ok

??

Page 3: Expression problem

Goals

● Adding a row (new type)● Adding a column (new operation)

Area Perimeter Draw

Square OK OK

Circle OK Ok

Triangle

Page 4: Expression problem

Constraints

● Extensibility● Code-level modularization

– no patching of existing code

● Separate compilation– independent deployability

● Static type safety (?)

Page 5: Expression problem

Basic class-based OO solution

interface Shape { double area(); double perimeter();}

class Square implements Shape { int a;

public Square(int a) { this.a = a; }

public double area() { return a*a; }

public double perimeter() { return 4*a; }}

Page 6: Expression problem

Basic class-based OO solution

class Circle implements Shape { int r;

public Circle(int r) { this.r = r; }

public double area() { return r*r*PI; }

public double perimeter() { return 2*r*PI; }}

class Triangle implements Shape { … }

Page 7: Expression problem

Basic class-based OO solution

● Adding a row (new type) → Easy

Area Perimeter Draw

Square OK OK ?

Circle OK Ok ?

Triangle OK OK ?

Page 8: Expression problem

Basic class-based OO solution

interface Shape { double area(); double perimeter();

void draw(); // New Operation

}

Page 9: Expression problem

Basic class-based OO solution

● Adding new row (new shape) → Easy● Adding a column (new operation) → Hard

Area Perimeter Draw

Square OK OK FAIL

Circle OK Ok FAIL

Triangle OK OK

Page 10: Expression problem

Basic procedural solution abstract class Shape { enum Type { SQUARE, CIRCLE } abstract Type type(); }

class Square extends Shape { public final int a; Square(int a) { this.a = a; }

Type type() { return SQUARE; } }

class Circle extends Shape { public final int r; Circle(int r) { this.r = r; }

Type type() { return CIRCLE; } }

static double area(Shape shape) { switch (shape.type()) { case SQUARE: return ((Square)shape).a * ((Square)shape).a; case CIRCLE: return ((Circle)shape).r * ((Circle)shape).r * PI; ... } }

Page 11: Expression problem

Basic procedural solution

static double perimeter(Shape shape) { switch (shape.type()) { case SQUARE: return 2 * ((Square)shape).a; case CIRCLE: return 2 * ((Circle)shape).r * PI; ... }}

static void draw(Shape shape) { switch (shape.type()) { case SQUARE: drawSquare(shape); case CIRCLE: drawCircle(shape); ... }}

Page 12: Expression problem

Basic procedural solution

● Adding a column (new operation) → Easy

Area Perimeter Draw

Square OK OK OK

Circle OK Ok Ok

Triangle ? ?

Page 13: Expression problem

Basic procedural solution

abstract class Shape { enum Type { SQUARE, CIRCLE, TRIANGLE } abstract Type type();}

static double perimeter(Shape shape) { switch (shape.type()) { case SQUARE: return 4 * ((Square)shape).a; case CIRCLE: return 2 * ((Circle)shape).r * PI; case TRIANGLE: … // New Shape ... }}

Page 14: Expression problem

Basic procedural solution

● Adding new row (new shape) → Hard● Adding a column (new operation) → Easy

Area Perimeter Draw

Square OK OK OK

Circle OK Ok Ok

Triangle FAIL FAIL

Page 15: Expression problem

OO with „visitor like” double dispatching

interface Visitor { void visitCircle(Circle circle); void visitSquare(Square square);}

class Square { public final int a; public Square(int a) { this.a = a; }

void accept(Visitor visitor) { visitor.visitSquare(this); }}

class Circle { public final int r; public Circle(int r) { this.r = r; }

void accept(Visitor visitor) { visitor.visitCircle(this); }}

class AreaCalculator implements Visitor { public double result;

public void visitCircle(Circle circle) { result = circle.r * circle.r * PI; } public void visitSquare(Square square) { result = 4 * square.a; }}

class PerimeterCalculator implements Visitor { ... }class Artist implements Visitor { ... }

Page 16: Expression problem

OO with „visitor like” double dispatching

● Adding a column (new operation/visitor) →Easy

● Adding new row (new shape) → Hard

Area Perimeter Draw

Square OK OK OK

Circle OK Ok Ok

Triangle FAIL FAIL

Page 17: Expression problem

Open classes

class Square { def a def area() { a * a } def perimeter() { 4 * a}}

class Circle { def r def area() { r * r * PI } def perimeter() { 2 * r * PI }}

Page 18: Expression problem

Open classes

Circle.metaClass.draw = { println "Drawing circle: $delegate"}

Square.metaClass.draw = { println "Drawing square: $delegate"}

def circle = new Circle(r: 12)circle.draw()

def square = new Square(a: 5)square.draw()

Page 19: Expression problem

Multi-methods

void m(SomeType object) {

// Single dispatch

object1.method(object2);}

Page 20: Expression problem

Multi-methods

void m(SomeType object) {

// Single dispatch

object1.method(object2);}

Receiver

Page 21: Expression problem

Multi-methods

interface Physical { void collideWith(Physical object); }

class Asteroid implements Physical { public void collideWith(Physical object) { // .. } }

class SpaceShip implements Physical { public void collideWith(Physical object) { // .. } }

spaceShip.collideWith(asteroid); // same action spaceShip.collideWith(spaceShip2); // same action

Page 22: Expression problem

Multimethods(defmulti collide (fn [this that] [(:type this) (:type that)]))

(defmethod collide [:SpaceShip :Asteroid ] [a b] "ship crashed by asteroid")(defmethod collide [:Asteroid :SpaceShip] [a b] "ship crashed by asteroid")(defmethod collide [:SpaceShip :SpaceShip] [a b] "ship ship encounter")(defmethod collide [:Asteroid :Asteroid ] [a b] "asteroid hit other asteroid")

(def ship1 {:type :SpaceShip :x 12 :y 46 :fuel 12 :rocket 45 :life 4})(def ship2 {:type :SpaceShip :x 24 :y 18 :fuel 62 :rocket 25 :life 7})(def asteroid {:type :Asteroid :x 4 :y 5})

(collide ship1 asteroid)(collide ship1 ship2)

Page 23: Expression problem

Multimethods

(def square1 {:type :Square :a 5})(def circle1 {:type :Circle :r 4})

(defmulti area (fn [this] [(:type this)]))(defmethod area [:Square] [this] (* (:a this) (:a this)))(defmethod area [:Circle] [this] (* 3.14 (* (:r this) (:r this))))

(defmulti perimeter (fn [this] [(:type this)]))(defmethod perimeter [:Square] [this] (* 4 (:a this)))(defmethod perimeter [:Circle] [this] (* 3.14 (* 2 (:r this))))

(area square1)(perimeter square1)

(area circle1)(perimeter circle1)

Page 24: Expression problem

Multimethods

; adding new Shape

(def triangle1 {:type :Triangle :a 2 :b 3 :c 4})

(defmethod area [:Triangle] [this] (triangle-area this))(defmethod perimeter [:Triangle] [this] (triangle-perimeter this))

; adding new operation

(defmulti draw (fn [this] [(:type this)]))

(defmethod draw [:Square] [this] (draw-square))(defmethod draw [:Circle] [this] (draw-circle))(defmethod draw [:Triangle] [this] (draw-triangle))

Page 25: Expression problem

Further solutions

● Clojure protocols● https://vimeo.com/11236603

● CLOS generic functionshttps://en.wikipedia.org/wiki/Common_Lisp_Object_System

● Type classes● https://en.wikipedia.org/wiki/Type_class

Page 26: Expression problem

References

● C9 Lectures: Dr. Ralf Lämmel - Advanced Functional Programming - The Expression Problem

● https://channel9.msdn.com/shows/Going+Deep/C9-Lectures-Dr-Ralf-Laemmel-Advanced-Functional-Programming-The-Expression-Problem/

● The Expression Problem, Philip Wadler, 12 November 1998

● http://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt

● Clojure's Solutions to the Expression Problem

● https://www.infoq.com/presentations/Clojure-Expression-Problem

● Clojure 1.2 Protocols, Stuart Halloway

● https://vimeo.com/11236603