[swift-evolution] [Draft] Allow declaration of abstract functions and properties on classes

David Waite david at alkaline-solutions.com
Wed Feb 24 18:33:35 CST 2016


I personally would rather Swift double-down on mixin/trait behavior and protocol-oriented programming rather than supporting abstract classes.

Exposing “Base” classes are an anti-pattern in my opinion (for reasons details more below my sig for TL;DR reasons)

Swift is a multi-paradigm type system with both traditional class inheritance and protocol-oriented programming w/ light trait support, but I would prefer features not needed for objc compatibility have parity between reference and value types, meaning that developer get tools to aid protocol implementation (usable by classes, structs and enums) rather than via class inheritance (unusable by structs and enums).

-DW

You can model all external interactions as being via explicit and implicit protocols. By this I mean -when you declare a class/struct/enum, you could consider the methods you declare to be defining a form of implicit protocol for usage.

For value types it is not possible for this implicit protocol to be leveraged in other contexts, but for classes it becomes possible for you to support this protocol by subclassing. At this point, you have gone from implicitly defining a protocol for usage, but also implicitly defining the behavior of this protocol based on the implementation of the class - the Liskov substitution principle in action.

You have several things fall out of having subclass relationships propitiate these implicit protocols:
- “missing super call” warnings/errors if the mechanisms in the super class are not being used, risking breaking the implicit contract.
- abstract methods (and thus abstract classes) when your use of classes is solely to define the implicit protocol interface and behavior, and not to define a working root class.
- protected-level access to define an interface to separate out the implicit public protocol from an implicit internal protocol for subclass customization of behavior
- required/convenience methods (most commonly initializers) to attempt to minimize the number of methods which need to be handled in subclasses to get new behavior, and/or define when subclass behavior may be invoked multiple times due to the base class calling the public interface

My own opinion is that a “robust” class design allowing inheritance is hard to create when using the semantics given to you by mainstream languages. This difficulty is specifically due to the number of implicit factors you are managing:
1. the API and underlying behavior for public usage
2. API and behavior for subclasses to implement
3. internal API for subclasses to use as part of their implementation
4. internal API for a class’s own private usage
5. internal API for other code within a library/framework
6. substitutability of operations acting between multiple instances of an object (a classic example is implementing equality)

> On Feb 24, 2016, at 3:54 PM, Gwendal Roué via swift-evolution <swift-evolution at swift.org> wrote:
> 
> +1.
> 
> A previous discussion on the topic : https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005728.html <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005728.html>
> 
> Gwendal
> 
> Le 24 févr. 2016 à 17:36, Evan Maloney via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> a écrit :
> 
>> After Thorsten reminded me yesterday that I wanted this feature too, I spent some time writing up proposal to allow declaration of abstract functions and properties on Swift classes:
>> 
>> https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c>
>> 
>> This is the first draft; all comments welcome!
>> 
>> Thanks,
>> E.
>> 
>> ---
>> 
>> Allow declaration of abstract functions and properties on classes
>> 
>> Proposal: SE-NNNN
>> Author: Evan Maloney <https://github.com/emaloney>
>> Status: Draft
>> Review manager: TBD
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#introduction>Introduction
>> 
>> Swift protocols are useful for the declaring interfaces that must be provided by conforming entities (in other words: structs, classes and enums). Adopters of a given protocol are free to provide any implementation they wish, so long as it supplies the necessary interfaces.
>> 
>> Separating interface from implementation is widely considered to be a best practice in software design, and the Swift protocol is designed for this use-case.
>> 
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-problem>The Problem
>> 
>> 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.
>> 
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#proposed-solution>Proposed Solution
>> 
>> The proposed solution involves adding support for abstract classes to Swift.
>> 
>> This would entail:
>> 
>> Allowing functions and properties to be declared abstract. An abstract function or property declares the interface without specifying the implementation.
>> 
>> Allowing abstract classes to be defined by partially unimplemented protocol conformances. If a class declares conformance to a protocol without providing an implementation for each of that protocol's properties and functions, it is an abstract class.
>> 
>> Requiring classes to be explicitly declared as abstract if it has one or more unimplemented functions or properties.
>> 
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#detailed-design>Detailed Design
>> 
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-functions>Abstract functions
>> 
>> Functions can be declared abstract using the abstract keyword, which must appear before the func keyword in the declaration. Otherwise, the notation is identical to how the function would be declared if it were to appear in a protocol:
>> 
>> public abstract func retrieveText() -> String
>> As long as the abstract keyword appears before the func, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.
>> 
>> The following declaration is equivalent to the one above:
>> 
>> abstract public func retrieveText() -> String
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-properties>Abstract properties
>> 
>> Abstract property declarations are identical to what would be found in a protocol, but are prefixed with the abstractkeyword, which must appear first:
>> 
>> abstract var fileName: String { get }
>> abstract var favoriteColor: UIColor { get set }
>> As is typical with protocol declarations, var is always used and not let.
>> 
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#abstract-protocol-conformance>Abstract protocol conformance
>> 
>> A class can be made abstract by declaring conformance to a protocol that it does not implement fully.
>> 
>> For example, say you had a protocol Vehicle:
>> 
>> protocol Vehicle
>> {
>>     var name: String { get }
>>     var color: UIColor { get }
>>     var numberOfWheels: Int { get }
>>     var isParked: Bool { get set }
>> 
>>     func driveTo(destination: Location) throws
>> }
>> In your code, you're able to factor out everything except the driveTo() function, the implementation of which is vehicle-specific. The common code goes into a BaseVehicle class:
>> 
>> abstract class BaseVehicle: Vehicle
>> {
>>     let name: String
>>     let color: UIColor
>>     let numberOfWheels: Int
>>     var isParked: Bool
>> 
>>     init(name: String, color: UIColor, numberOfWheels: Int, isParked: Bool = true)
>>     {
>>         self.name = name
>>         self.color = color
>>         self.numberOfWheels = numberOfWheels
>>         self.isParked = isParked
>>     }
>> }
>> The BaseVehicle class partially conforms to the Vehicle protocol: the name, color, numberOfWheels and isParked properties are provided, but the driveTo() function remains unimplemented.
>> 
>> As a result, BaseVehicle is an abstract class and must be declared as such.
>> 
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#the-abstract-class-declaration>The abstract class declaration
>> 
>> A class must be declared as abstract if any of the following are true:
>> 
>> If the class declares one or more abstract functions or properties
>> If the class declares conformance to a protocol but does not supply implementations for every one of the functions and properties declared in that protocol.
>> If the class inherits from an abstract class and does not supply an implementation for every one of the unimplemented functions or properties.
>> Classes are marked as abstract by placing the abstract keyword before the class keyword at the top of the class declaration, eg.:
>> 
>> public abstract class MyAbstractClass
>> {
>>     // ...code...
>> }
>> As long as the abstract keyword appears before the class, the order of appearance of the abstract keyword relative to any public, private or internal access modifiers is not meaningful.
>> 
>> The following declaration is equivalent to the one above:
>> 
>> abstract public class MyAbstractClass
>> {
>>     // ...code...
>> }
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#instantiation>Instantiation
>> 
>> Because an abstract class is not a complete implementation, the compiler will not allow instantiation of abstract classes.
>> 
>> Attempting to instantiate an abstract class will result in a compiler error.
>> 
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#impact-on-existing-code>Impact on Existing Code
>> 
>> None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.
>> 
>>  <https://gist.github.com/emaloney/d0b5bf7dd831d4f7415c#citations>Citations
>> 
>> This idea has been discussed in the following swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution> mailing list threads:
>> 
>> [Review] SE-0030 Property Behaviors <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160222/010876.html>
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160224/b5fadda3/attachment.html>


More information about the swift-evolution mailing list