[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