Expression problem related to OO and FP

Reid Evans
3 min readFeb 26, 2019

--

This was written somewhere else, by someone who probably did a better job explaining it than I’m doing. But I can’t find that link, so here goes…

Photo by Olav Ahrens Røtne on Unsplash

In the common ways in which problems are deconstructed in OO and FP there are tradeoffs in the future extensibility of the program. Take the following two examples, in pseudo Haskell and C# respectively.

Haskell, FP approach

type Shape
= Square
| Circle
display :: Shape -> String
display Square = "Square"
display Circle = "Circle"
sides :: Shape -> Int
sides Square = 4
sides Circle = 1

C#, OO approach

interface IShape {
string Display();
int Sides();
}
class Square : IShape {
public int Sides() { return 4; }
public string Display() { return "Square"; }
}
class Circle : IShape {
public int Sides() { return 1; }
public string Display() { return "Circle"; }
}

In both cases we have 2 inhabitants of a type and 2 functions that can act upon all inhabitants of the type.

Adding a new Shape

Now imagine we want to add a new shape. In the OO approach we simply add another class that implements IShape and we’re done; there’s no need to touch Square, Circle, or IShape.

class Triangle : IShape {
public int Sides() { return 3; }
public string Display() { return "Triangle"; }
}

The FP approach requires more of a refactor. We have to add a case to our Shape type and update all functions that know about our Shape type.

type Shape
= Square
| Circle
| Triangle
display :: Shape -> String
display Square = "Square"
display Circle = "Circle"
display Triangle = "Triangle"
sides :: Shape -> Int
sides Square = 4
sides Circle = 1
sides Triangle = 3

In the case of adding inhabitants to a type, the OO approach is far less work than the FP approach. In fact, this is exactly where OO shines because it organizes code in such a way that it easily allows future inhabitants of types.

Adding a new function

Now let us imagine that we need to do something else with our Shape type. Let’s say that Square and Circle are all the shapes we need and we now need to be able to calculate their areas.

In the FP approach this change is trivial

area :: Shape -> Number -> Number
area Square x = x * x
area Circle x = 3.14 * (x * x)

This time it’s the OO approach that requires significant refactoring as we have to update the interface and all classes implementing the interface.

interface IShape {
string Display();
int Sides();
double Area(double radius);
}
class Square : IShape {
public int Sides() { return 4; }
public string Display() { return "Square"; }
public double Area(double radius) { return radius * radius; }
}class Circle : IShape {
public int Sides() { return 1; }
public string Display() { return "Circle"; }
public double Area(double radius) { return 3.14 * (radius * radius); }
}

Summary

FP code tends to favor Algebraic Data types which have a fixed number of type inhabitants but that can have any number of functions that act upon the type. OO code tends to favor interfaces with a fixed number of functions, which allow any number of classes to implement the interface.

--

--