# How to tackle extensibility considering the Data/Object Anti-Symmetry?

In Clean Code by Uncle Bob, page 124-125 he states

Procedural code (code using data structures) makes it easy to add new functions without changing the existing data structures. OO code, on the other hand, makes it easy to add new classes without changing existing functions.

Jörg W Mittag says that this has been termed as the Expression Problem by Phil Wadler, but he states that to solve the problem we should

define a datatype by cases, where one can add new cases to the datatype and new functions over the datatype, without recompiling existing code, and while retaining static type safety [...]

I'm more interested in the extensibility of a software, i.e. if it's "easy" to add new functionalities incrementally. I have an intuitive definition of "easy" in this case, and it includes bug resistance and no duplicate code.

Here is Uncle Bob examples from the book

Polymorphic Shapes

``````public class Square implements Shape {
private Point topLeft;
private double side;

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

public class Rectangle implements Shape {
private Point topLeft;
private double height;
private double width;

public double area() {
return height * width;
}
}

public class Circle implements Shape {
private Point center;
public final double PI = 3.141592653589793;

public double area() {
}
}
``````

Procedural Shape

``````public class Square {
public Point topLeft;
public double side;
}

public class Rectangle {
public Point topLeft;
public double height;
public double width;
}

public class Circle {
public Point center;
}

public class Geometry {
public final double PI = 3.141592653589793;

public double area(Object shape) throws NoSuchShapeException {
if (shape instanceof Square) {
Square s = (Square) shape;
return s.side * s.side;
}

else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle) shape;
return r.height * r.width;
}
else if (shape instanceof Circle) {
Circle c = (Circle) shape;
}
throw new NoSuchShapeException();
}
}
``````
• Jörg W Mittag points out something interesting here, the "problem" Uncle Bob says in this case that is "difficult" to add functions or classes depending how you choose to start. But why is it difficult? Because you have to recompile something you didn't change? Because you have to write duplicate code? – Jp_ May 2 '18 at 9:48

This has been termed the Expression Problem by Phil Wadler, although it is much older than the discussion in which he came up with this term. Solving it is one of the "holy grails" of Programming Language Design.

One of the problems with something that is so famous is that everybody comes up with their own definitions, so talking about it, without rigorously defining it, makes little sense, except to invite flamewars.

1. Extend the code with new operations on the existing kinds of data.
2. Extend the code with new kinds of data that work with the existing operations.
3. This should be true extension, i.e. no modification of existing code.
4. It should be statically type-safe.

Ruby-style mutable Open Classes with Monkeypatching or ECMAScript-style mutable Prototypes solve problems 1 and 2. We can argue about whether they really solve point 3, though: If you mutate a class in memory, but the code that performs this mutation is in a separate file … is that extension or modification of existing code?

So, let's say, they solve 2.5 of the problems, but they are obviously not statically type-safe.

CLOS-style multimethods solve 3 fully, but still fail at 4. I think both MultiJava and Tuple Class-style multimethods might qualify, but I am not too familiar with them.

Haskell's Typeclasses were the first system to satisfy all 4 constraints.

Martin Odersky et al. added another two constraints:

1. It should be modular, i.e. the extension should be in a separate module (in the PLT sense of the word) that is separately deployed.
2. And this includes modular typechecking.

Haskell Typeclasses actually fail at #6. Scala implicit values and implicit conversions, however, do solve all 6 problems.

Note: there may be other features in Haskell that solve all 6, but I am not too familiar with all the extensions and research that have been done since Typeclasses.