### 20 November 2018 / Adam Gajek

# OOP vs. FP. The pursuit of extensibility part #2

In my previous blog post, I introduced the definition of the Expression Problem by Philip Wadler, along with two implementations of its solution. One of them is based on subtyping polymorphism (the so-called OOP solution); the other, which is implemented via functions with pattern matching, merits the title of a Functional Programming solution. We’ve seen that both solutions have some drawbacks when it comes to some specific ways of extending code. In the Expression Problem’s definition, we are specifically concerned with extending the data model (forms) and behaviors (operations).

In this blog post, I’ll show the Expression Problem’s **real** Functional Programming solution, which is based on the Type Class pattern.

**Type Class 101**

If you are not familiar with Type Class or you want to get to know more about its implementation details in Scala, I strongly recommend you read my article on how to build it from scratch in Scala.

Technically Type Classes in Scala are just traits parameterized with some type T. We provide the implementation of interfaces for types we would like to have an instance of. The difference between subtyping and Type Class is that we are not implementing an interface inside the class T itself, but instead supply an implementation that is defined outside the type T. Thus, we are even able to provide a Type Class instance for classes for which we don’t have access to the source code (such as external libraries available as jar packages).

Then we use our Type Class instances as implicitly provided references to objects for some type T, and we define functions parameterized with that type. In this way, we are not constrained to any specific type. The only requirement is to provide the Type Class instance for this type, as we see below:

1 |
def pairEquals[A](a: A, b: A)(implicit eq: Eq[A]): Option[(A,A)] |

Here the method takes two arguments of some type ** A** and requires the instance of Type Class

**to be available in the implicit scope of the method invocation.**

`Eq`

**Expression problem initialization**

**Model definition**

The vital feature of a Type Class is that a separation can be performed on the data and operations. Similarly to the FP approach in part #1, we’ll start by defining the model that describes what our domain looks like.

1 2 3 |
case class Number(a: Double) case class Add[A, B](a: A, b: B) |

With the above data types, we are able to define the arithmetic expression like this:

`1 + (1 + 2)`

in the following code

1 |
Add(Number(1), Add(Number(1), Number(2)) |

Ok, we’ve implemented the domain of an arithmetic expression using the syntax of Scala, and now we need to implement a way of performing **operations** on the already defined **forms.**

**First behaviors**

As you know, the Type Class pattern has two sides: Type Class definition and Type Class instances for selected types.

*Type Class definition can be implemented as a parameterized trait with a little help from the macro-based library* *Simulacrum**, which generates necessary boilerplate to make using Type Classes in Scala effortless.*

We’ll start with the implementation of the first operation, which is to evaluate our Expression into a single value of type Double. This evaluation is nothing more than applying arithmetic operations to numbers, thus ending up with a result in the form of a single value.

**Type Class definition**

1 2 3 |
@typeclass trait Eval[A] { def eval(expression: A): Double } |

The @typeclass annotation comes from Simulacrum and suggests that the following trait is a Type Class definition that should have generated methods which I described in my first article on this subject.

As I mentioned in the introduction, the Type Class definition comes in the form of Scala’s type-parameterized trait.

**Type Class instances**

Now that we have the definition of ** Eval** in place, the next step is providing implementations for Expression’s forms:

`Number`

and `Add`

:
1 |
implicit val numberExpr: Eval[Number] = (expression: Number) => expression.a |

We’ve created an ** Eval** Type Class instance for

**, which evaluates by unpacking inner field**

`Number`

**a**

**.**

This lambda expression is transformed into an implementation of an anonymous class (which extends trait Eval with one abstract method) thanks to the so-called Single Abstract Method feature of Java 8.

We have the implementation of ** Eval** for the

**type, so the next natural step is to provide it for**

`Number`

`Add`

**as well:**

1 2 3 4 |
import Eval.ops._ implicit def addExpr[A: Eval, B: Eval]: Eval[Add[A, B]] = (expr: Add[A, B]) => expr.a.eval + expr.b.eval |

The body of this function is quite easy to understand and is not very interesting. We are able to invoke eval because of the feature called **Extension Methods** provided by

generated by Simulacrum.**import Eval.ops._**

We just use the ** Eval[A]** and

**Eval[B]**

Type Class *instances*(provided implicitly by the compiler thanks to

**Context Bounds**):

1 |
implicit def addExpr[A: Eval, B: Eval] |

We then delegate evaluation of both sides of the expression and result of the Add operation to the type class instances.

The interesting part here is that we don’t have to provide a Type Class instance for every type or type combination in this case, such as

and **Add[Number, Add]**

.**Add[Add[Number, Number], Number]**

We might think that here we have a two-step Type Class derivation. We implement the Type Class instance for ** Add** and assume that somewhere in its implicit scope there will be further instances of

**for**

`Eval`

**and**

`A`

**.**

`B`

Now we can use our helper method to evaluate an expression

1 |
Add(Number(1), Add(Number(1), Number(2))).eval |

**The ultimate extensibility of Type Class**

Using Type Classes, I’ll now show how we can introduce extensibility into our code in terms of adding the new forms and the new operations.

**New Operation**

As in the previous part of this series, we aim to extend our arithmetic expression project with the ability to visualize expressions in a textual form.

To do so, we need an additional Type Class which is responsible for transforming an **e****xpression **to a single value of type **String**:

1 2 3 |
@typeclass trait Show[A] { def print(expr: A): String } |

Now we need to provide instances of `Show`

** **for types `Add`

** **and ** Number**. As implementation of the instance for

`Number`

**is trivial, I’ll show only the one for**

**:**

`Add`

1 2 3 4 |
object Show { implicit def addShow[A: Show, B: Show]: Show[Add[A, B]] = (expr: Add[A, B]) => expr.a.print + " + " + expr.b.print } |

As you can see, there is no significant difference between the definitions of the Type Classes `Eval`

** **and `Show`

**. **Derivation of an instance for this Type Class follows the same mechanism of applying implicit parameters as previously, so if anything isn’t clear, please go back to the description of the implementation of `Eval`

** **for ** Add**.

We have already added a new way of interpreting our expression without modifying the existing code, so we have the proof that Type Classes are doing well in terms of extending code by adding new operations.

**New Form**

Now we need to check how our new implementation technique copes when new forms are added to the data model.

In line with the previous post, we will add the ability to express the multiplication operation in our arithmetic expressions system:

1 |
case class Mul[A, B](a: A, b: B) |

We have implemented two operations represented by Type Classes `Eval`

** **and ** Show**; therefore, as we’ve just added the new form, we need to provide instances of these Type Classes for the type

`Mul`

**:**

1 2 3 4 |
import Eval.ops._ implicit def mulExpr[A: Eval, B: Eval]: Eval[Mul[A, B]] = (expr: Mul[A, B]) => expr.a.eval * expr.b.eval |

1 2 |
implicit def mulShow[A: Show, B: Show]: Show[Mul[A, B]] = (expr: Mul[A, B]) => expr.a.print + " * " + expr.b.print |

We’ve added the completely new form of our expression and we are able to implement every operation that we defined previously.

**Conclusions**

In the previous blog post, I showed that subtyping and pattern matching didn’t provide a solution for the problem of two-dimensional extensibility. When it comes to adding new implementations of an interface/trait, subtyping does the job; however, when we want to add a new operation to a trait, we have to make changes in all the existing implementations.

By contrast, the implementation based on pattern matching handled adding new operations well; however, when we introduced a new subtype, we needed to make a change in each function to avoid a **MatchError**.

Fortunately, in this article, we have seen that the introduction of Type Classes has solved both our problems with extensibility. We were able to extend our logic by adding new operations and forms, but without modifying anything in the existing code.

Adding new forms | Adding new operations | |

Subtyping | EASY | DIFFICULT |

Pattern Matching | DIFFICULT | EASY |

Type Class | EASY | EASY |

## Leave a Reply