Swift's "Final" Countdown
And the inevitable deprecation of inheritance-based polymorphism
There’s an interesting discussion happening currently regarding a proposal on the swift-evolution mailing list to make Class types final by default in Swift. The initial proposal references the occasional need for a reference type that is not intended to be subclassed as the primary motivation.
There are some developers who are clearly not in favor of the idea, particularly because it limits their ability to subclass buggy bits of UIKit to find or improve functionality. This use-case of modifying through extension a single component of a framework that isn’t behaving as desired, is exactly what class polymorphism is for. And viewed through the lens of past experience with UIKit as it exists today, it’s easy to understand why these developers are concerned.
On the other side, engineers within Apple seem to have already developed considerable momentum and support for this final-by-default idea, with one of the arguments being that as the maintainers of the most used frameworks on iOS (used by literally every single app), Apple engineers are forced to support the workarounds and subclassed solutions used by major apps which were implemented in a way that abused the design of the original components. This has limited Apple’s ability to fix and evolve components over time, because there are subclasses out in the wild which depend on the implementations within UIKit, Foundation, etc. not just the interfaces. An admittedly frustrating and painful position.
Initially, my reaction as someone who has written a lot of OOP code and architecture over the years was that disabling inheritance by default would break several principles of OOP — principles like open/closed (the “O” in SOLID) and template methods. And it’s true. This change would fundamentally discourage long-held OOP practices and mentalities.
But stepping back further, it’s important to separate the goals of OOP (reusability, maintainability, extensibility) from the approaches used to achieve those goals in the past, which were largely evolved based on the limitations and personalities of the most commonly used languages for the last 20 years.
When it comes right down to it, inheritance is only one approach to reusability and extensibility, and almost anyone will admit, it’s the worst approach following outright method swizzling at runtime. Swift has already introduced two powerful new ways of reusing implementations: generic programming and protocol extensions, and while they can’t cover every case yet, they can cover the vast majority of cases and will probably be extended in the near future to cover the rest.
Given these tools, which are safer, more explicit, and less fragile than inheritance, it makes sense that Swift will approach OOP goals with different and better paradigms.
Looking at the original proposal again, which discusses the need for a non-inheritable reference type, I see that Apple’s engineers are looking far beyond that narrow need, and looking directly at the question of why and when do we need inheritance at all? Inheritance has always been discouraged in favor of composition, but it hasn’t always been possible to avoid it.
Now, in Swift, we can say “Favor composition, generic implementations, and protocol extensions over inheritance”, and with such a long and nearly complete list of “favored” alternatives to inheritance, the question is now becoming: “What advantages does inheritance really give us at all, and when should it ever be used?”. And I’m not sure there are many answers to that question that couldn’t be better answered with another, newer solution enabled by Swift’s (and other modern languages’) features. Solutions with far less baggage and far fewer downsides.
It’s as clear as the rising sun that Apple’s framework engineers are completely rethinking UIKit and everything else in light of new approaches offered by Swift. So to those developers who worry about their ability to modify framework behavior as needed in the future to work around bugs or problems, I say this: don’t worry, because the new versions of those frameworks Apple will certainly be releasing in the future will enable that kind of extensibility through other means. Yes, inheritance will likely go away and not be a means to modify UIKit behavior. But means always come and go, and as long as the goal of extensibility and reusability is supported by a new means, is there really any problem here?
For me at least, this debate about “final-as-default” for classes is more and more looking like a non-debate. It’s looking like a long-needed opportunity to find a way to finally change the somewhat subjective statement of “Favor composition (and generic implementations and protocol extensions) over inheritance” to simply be “Here’s how to use composition, generic implementations and protocol extensions”.
The goals of OOP aren’t going away and will still be relevant for a long time. But the patterns and principles to achieve those goals, long enshrined in 20-year-old books that reference C and C++, will evolve (as things do) into something far better.
Posted in: architectureiosoopswift