In Swift Talk 88, Brandon Kase showed us the tradeoffs of designing libraries with enums or classes.
When an external library defines its data types as classes, it is easy for us to add a new case: we create a subclass, and override all the necessary methods. An example of this is in UIKit: we can add a new
UIView subclass and adjust everything to fit our needs.
When an external library defines their data types as enums, we cannot add a new case in our own code, unless we fork the library. For example, if a drawing library defines its primitives as enums, we have to work with the existing primitives.
On the other hand, we can think about new interpretations. In a class-based library, it’s not so easy to add a new interpretation to a type. For example, we cannot easily add a different rendering backend for UIKit (let’s say, render to text) while guaranteeing to stay type-safe and complete (i.e. cover all possible subclasses).
With the enum-based approach, it’s easy to add new backends: we switch on the enum, and implement each case.
The tradeoffs look like this:
Sometimes you want to be in the upper left corner (use enums) or bottom right corner (use subclassing), but what if you want to be in the top right?
These two axes of extensibility are the base of the “expression problem”: how can we extend an existing library by adding new cases as well as new interpretations without having to change the library, and while maintaining type-safety and completeness?
In the next Swift Talk (out this Friday) we’ll look at how to solve the expression problem using protocol composition. To watch all our episodes, subscribe.