Over the last few years, I’ve been interested in the Scala programming language and I’ve heard a lot of criticism about its mixed Functional and Object-Oriented Programming nature. On the other hand, Functional Programming has recently become so popular that OOP is now considered an old-fashioned method that should be translated to FP as soon as possible.

In this blog post, I’ll try to compare both approaches in terms of extensibility based on the Expression Problem formalized by Computer Science Professor, Philip Wadler, who greatly contributed to the development of Functional Programming.

Extensibility

We all know that our code is evolving all the time. There is always some refactoring, bug-fixing and (hopefully) additions of new features. Moreover, the majority of bugs, and problems with the code in general stem from the fact that we constantly make changes.

Many of these changes might have been written by your teammates (present or former) a long time ago. There is no way to avoid modification of our code, but we can optimize the number of necessary amendments when adding new features by simply making our code extensible.

Software extensibility is vital so that one of the famous SOLID principles is dedicated solely to this subject, as is formulated by Bertrand Mayer’s Open-Close Principle

SOFTWARE ENTITIES (CLASSES, MODULES, FUNCTIONS, ETC.) SHOULD BE OPEN FOR EXTENSION, BUT CLOSED FOR MODIFICATION.

In simple words, we need to create code in such a way that when we want to add something new, we should not be obliged to touch anything written in past.

The Expression Problem

The Expression Problem focuses on one of the most important features of code: extensibility. This can be described as the ability to evolve without:

affecting the clarity of the existing code

putting much effort into adding new functionality

breaking code that works properly

For a long time I’ve been treating extensibility as a single, unidirectional virtue of code, but when I learned about the Expression Problem I realized that there are actually two separate ways in which we can extend the code.

According to professor Wadler, the first direction we can follow is the ability to extend our code by adding new forms. This can be simply described as providing brand new implementations of current interfaces.

The second direction is adding new operations. Operations, as you probably suspect, express functionality represented as a new method in the interface.

The first steps

To test the extensibility of our code, first we need to have something to extend. Our initial example will consist of one operation, represented as an evaluation of an arithmetic expression. We represent it as a recursive data structure of classes that encode expression at each level and tells us what to do with its operands:

val expression = Add(Add(Number(2), Number(3)), Number(4))

Here we model with objects of types Add and Number following the arithmetic expression:

(2 + 3) + 4

Our task is to evaluate or (in more mathematical terms) reduce our expression to a single value of a Double.

The Object-Oriented Programming approach

Let me start with the OOP way of performing polymorphism, so-called subtyping polymorphism.

Let’s start with the fact that an expression can have multiple forms (we can say it is polymorphic); to link these forms together we need to create a common abstraction interface, which in Scala is defined as a trait.

trait Expr {

def eval: Double

}

Our trait defines one operation that can be performed on each form of an expression. We’ve called this operation eval and its task is to reduce the whole expression to the final term of a value of a Double. A few paragraphs above we decided to handle two forms of expression: Number and Add.