Hey all — this is my first reply to this list, so please forgive me if something goes wrong.
I wanted to elaborate more on Nate Birkholz's earlier message about extending the 'required' keyword to class methods, other than only init.
I had suggested this to Joe Groff on Twitter, and encouraged Nate to go ahead and start the thread. I’m just now finding the time to respond My goal here is to flesh out this idea a bit more, and hopefully get a more engaging discussion started.
Subclassing in Objective-C has the following deficiencies:
(1) It lacks the ability to formally declare if a method must be overridden.
(2) When overriding a method, there is no mechanism to enforce a call to super. You could use NS_REQUIRES_SUPER, but not only is this barely discoverable, it isn’t part of the language. Further, it merely generates a warning when library authors most likely intend for this to be an error. (Ok, one could treat warnings as errors, etc. etc.)
(3) It is easy for clients to override a superclass method without knowing it. That is, subclasses can simply implement an identical selector of the superclass without any kind of warning.
Clearly this is problematic. What happens when a subclass does not call super, but should? I think the only answer is "undefined behavior". Typically, the (not so great) solution here is documentation. This is common in Cocoa and CocoaTouch, for example each time you (never) read the docs, you'll see things like "your implementation must call super at some point".
Swift improves on these deficiencies in the following ways:
(1) For public methods (or internal for classes in the same module), Swift requires the 'override' keyword. This is great. Clients are now aware that they are overriding a superclass method. Though this *should* be an indication to the client that super might need to be called and the docs should be read (lol), clients are not required to do either.
(2) Superclasses can prevent overriding using the 'final' keyword. This provides a lot of safety but results in an "all or nothing" decision for library authors.
(3) For initializers only, Swift enforces the 'override' keyword *and* a call to super due to Swift’s strict initialization rules. Thus, client subclasses can safely override initializers. Woo!
(4) Additionally, for initializers only, Swift superclasses can require that subclasses implement a specific initializer using the 'required' keyword (given that a subclass provides a custom init). Again, augmenting the subclass initialization flow can be done safely. ::applause::
OK. Hopefully I didn’t miss anything there. As you can see, there’s one major deficiency remaining in Swift:
- If a subclass chooses to override a non-final method of its superclass, there is no mechanism to require a call to super.
My proposed solution is straight-forward: extend the use of 'required' to methods, rather than only allow this for init.
Behavior would be the following:
- A superclass can specify a method as 'required'.
- *If* a client’s subclass chooses to override a 'required' method, it must call super.
- Without the 'required' keyword, current behavior would remain the same.
Comments from Joe via twitter:
- "Should be straight forward to implement."
- "One interesting thing it would enable is invariant implementations of covariant protocol requirements." <dogScience.png>
Thanks for reading!