Right. So that’s one of the other “solutions” to this problem in my talk — don’t use OOP at all, just use external functions. Which does work, but then you can no longer specialize or dispatch on the first argument, c1
and c
(let alone c2
). Moreover, the argument follows this trajectory:
- Here’s a problem that is hard in OOP.
- Solution: don’t use OOP at all, just use functions.
That is a solution, but it does not counter the fact that the problem is a problem for OOP, it instead shows that you can solve the problem by abandoning OOP entirely. In general, in functional programming it is easy to add new operations to existing types: functions don’t live inside of types like methods do in OOP, so you can just define a new external function to add an operation to an existing type. Problem solved. However, you have the opposite difficulty in the functional approach: it’s hard to make existing operations apply to new types. This is easy to do in OOP — you just subclass whatever the operation is defined on and add a specialized method.
Consider this add
function that you just defined in Python. It works great if c1
and c2
are ducklike enough to quack in response to accessing the .r
, .g
and .b
fields. But what if wanted to make the add
function work on something less ducklike that needed a different implementation? You need to edit the original add
function and add if/else type check for each new implementation that you want to support. Which works, but isn’t extensible: you need to modify the function for each new type you want to support. This is exactly the kind of problem that OOP was introduced to solve. Multiple dispatch doesn’t have any problem here: you can define add
like that and then just write more specialized methods as needed. No fuss, no muss.
In summary, the expression problem presents as different problems for object-oriented and functional programming paradigms:
- In OOP, it presents as difficulty adding new operations to existing types, which is easy when just using functions;
- In functional programming, it presents as difficulty making existing operations apply to new types, which is easy in OOP.
The magic of multiple dispatch is that both aspects are unproblematic: you can add new operations to existing types and extend existing operations to new types with equal ease and simplicity.