[swift-evolution] [Draft] Allow declaration of abstract functions and properties on classes
Brent Royal-Gordon
brent at architechies.com
Wed Feb 24 19:27:16 CST 2016
> Unfortunately, the protocol does not cover all of the cases where a developer might want to specify an interface to be implemented by another entity.
>
> For example, consider the class, which allows the creation of an inheritance hierarchy. Often, a class in a hierarchy exists merely to provide a common implementation to subclasses. Such classes aren't ever intended to be instantiated directly; only subclasses will be instantiated.
>
> To illustrate the point, imagine a view controller class that:
>
> • Places an animating UIActivityIndicatorView onscreen
> • Performs some operation to retrieve some text
> • Puts the text in a UITextView and places it onscreen
> • Hides the UIActivityIndicatorView
> Now imagine you had many cases in your application where you could benefit from such a view controller, and each case differed only in the operation required to retrieve the text (represented by Step 2 above).
>
> Ideally, you would be able to achieve this by declaring the interface for a function without needing to specify an implementation, the same way you would with a protocol:
>
> func retrieveText() -> String
> In other languages, such as C++, this concept exists in the form of an abstract class. However, Swift does not support this, so developers are forced to provide useless implementations such as:
>
> func retrieveText() -> String
>
> {
> fatalError(
> "Subclasses must implement retrieveText()"
> )
> }
>
> The idea here is that subclasses should always provide a retrieveText() implementation, and therefore the call to fatalError() should never be hit.
>
> This has a few significant downsides:
>
> • It forces the developer to write code that should never be executed under normal conditions. This seems like a waste.
>
> • Because a default implementation is provided--the one that calls fatalError()--the compiler has no way of knowing that the subclasses are supposed to provide an implementation, too.
>
> • If a subclass implementor forgets to provide a retrieveText() function, the error will not be caught until runtime, and not until a user navigates to the affected portion of the application. This may not occur until the application has shipped.
That's one alternative, yes. Others include:
1. Having a delegate provide the `retrieveText()` method.
2. Having a closure property implement the `retrieveText()` method.
3. Declaring a protocol `ActivityViewControlling` that requires `retrieveText()` and adding the other logic in an `extension ActivityViewControlling where Self: UIViewController`.
I think that 1 or 2 are usually the best way to handle something like this, but let's explore 3 for a minute, because that's a place where Swift could probably be improved.
Currently, Swift allows you to constrain a protocol to only class types:
protocol ActivityViewControlling: class {
func retrieveText() -> String
}
extension ActivityViewControlling where Self: UIViewController {
...
}
class MyActivityViewController: UIViewController, ActivityViewControlling {
func retrieveText() -> String { ... }
}
But when you do that, Swift permits you to use any class type, which is a bit weird semantically—ActivityViewControlling can be applied to any class, but it's really only meant to be applied to subclasses of UIViewController. An ActivityViewControlling type which isn't a view controller is kind of meaningless.
// Why can I do this?
class PossibleButUseless: ActivityViewControlling {
func retrieveText() -> String { ... }
}
Suppose instead we allow a protocol to require that the conforming class inherit from another class. We could then omit the `where` clause and possibly even make the inheritance itself implicit in conforming to the protocol:
protocol ActivityViewControlling: UIViewController {
func retrieveText() -> String
}
extension ActivityViewControlling {
...
}
class MyActivityViewController: ActivityViewControlling {
func retrieveText() -> String { ... }
}
This would also relieve some of the pressure on us to support class-plus-protocol typed variables. In many of these cases, *all* types conforming to the protocol should be subclasses of a particular class, but there's no way to express that in the type system. Subclass-only protocols would correct that shortcoming.
Subclass-only protocols would cover most, if not all, of the use cases of abstract classes, but I think they would be a less dramatic change to Swift, because protocols are already types which can't be instantiated and impose requirements on their subtypes. I therefore believe they would be a better, more Swift-like approach to the problem abstract classes are trying to solve.
--
Brent Royal-Gordon
Architechies
More information about the swift-evolution
mailing list