Oddbean new post about | logout
 This type of complexity is a common problem in object oriented programming. "This" type has a method that accepts a "that" type, and it works well until there are too many combinations of types. Any time a new "that" is created, the original code needs to be updated. This is where OOP reaches for interfaces, so the "that" type can be anything that claims to be a "that". This works well for even longer, but it still tends to fail at scale. Any time a new behavior is require, the interface changes and now every implementation needs to be updated. 

I have a soft spot for old systems, and one of them is Common Lisp. CL was standardized before OOP, and one of the ways it deals with this is multiple dispatch. Instead of "this" type of object having a method operating on "that" type, the function is declared abstractly in a namespace. "Intersect" would be a function that finds the intersecting parts of its arguments. 

Now when someone makes a new "that", they include code that knows how to intersect "that" with as many "this" types as they find useful. No original code needs to be changed. Later, someone working on "other thing" needs to intersect two types that no one has written intersection code for, so they write their own implementation that is applied to these two other pieces of code, and neither of them needs to be updated. 

Complexity can quickly get out of hand, but it's also something that we've been thinking about for a long time. The more we learn about history, the more opportunities we have to find good solutions to new problems.