[swift-evolution] [Returned for revision] SE-0117: Default classes to be non-subclassable publicly

Chris Lattner clattner at apple.com
Thu Jul 14 16:39:14 CDT 2016

Proposal link: https://github.com/apple/swift-evolution/blob/master/proposals/0117-non-public-subclassable-by-default.md

Hello Swift Community, 

The review of "SE-0117: Default classes to be non-subclassable publicly" ran from July 5…11, 2016.  As expected, this proposal was extremely polarizing, with valid arguments on both sides.  The opinions held by supporters and opposers are held very strongly, and hundreds of emails were generated in a healthy debate about this topic.

The review manager read every post on this topic, and the core team discussed this topic at length.  The core team concluded three things: 

 - First, the core team *agrees with conviction* that it is the right default for public classes to be non-subclassable outside their module, unless they carry some additional indication from the API author that the class was designed to be subclassed.
 - Second, there was insufficient discussion about anything *other* than the first point.  The proposal also contains the “overridable” concept, which is highly impactful, but lacked significant discussion.
 - Third, the core team agrees that the concrete syntax of “subclassable class” is … suboptimal.

On the first point, there are three related arguments against SE-0117:

- First is that clients of Apple frameworks often choose to subclass classes that Apple publicly documents as being “not for subclassing”, as a way of “getting their job done,” typically as a way to work around claimed bugs in Apple frameworks.  The core team and others at Apple feel that this argument is analogous to the argument that Swift should “support method swizzling by default”.  Swift loves dynamic features, but has already taken a stance against unanticipated method swizzling, by requiring an API author to indicate where they allow method swizzling with the ‘dynamic’ keyword.  Almost all classes vended by Apple APIs are subclassable (which isn’t changed by this proposal) so this argument is not compelling to the core team, nor is it consistent with the existing design of Swift.  It is also important to note that Cocoa also makes heavy use of delegation (via protocols) which allows client code to customize framework behavior without subclassing.

- Second is that clients of some other public API vended by a non-Apple framework (e.g. a SwiftPM package) may end up in a situation where the framework author didn’t consider subclass-ability, but the client desires it.  In this situation, the core team feels that a bigger problem happened: the vendor of the framework did not completely consider the use cases of the framework.  This might have happened due to the framework not using sufficient black box unit testing, a failure of the imagination of the designer in terms of use cases, or because they have a bug in their framework that needs unanticipated subclass-ability in order to “get a job done”.  Similar to the first point, the core team feels that the language is not the right place to solve this problem.  Instead, there is a simple and general solution: communicate with the framework author and get them to add the capabilities that you desire.  If they simply need to add subclass-ability, then that is no problem: it is a source-compatible change to a dependency.

- Third is a longer-term meta-concern, wherein a few people are concerned that future pure-Swift APIs will not consider subclass-ability in their design and will accidentally choose-by-omission to prevent subclass-ability on a future pure-Swift API (vended by Apple or otherwise).  The core team feels that this is an extremely unlikely situation for several reasons.  First of which is that it heavily overlaps the first two concerns.  More significantly, any newly-designed and from-scratch APIs that are intended for Swift-only clients will make use of a breadth of abstractions supported by Swift—structs, enums, protocols, classes.  The primary reasons to use classes in Swift are subclassability and reference semantics, so the core team feels that the likelihood of accidental omission is small.  Likewise, the decision to require every member of a public class to be marked public in Swift indicates a commitment (in line with SE-0117) that expects cross-module API authors to think carefully about the API they are authoring as well as their use cases.

To reiterate, as a summary, the core team *agrees with conviction* that it is the right default for public classes to be non-subclassable outside their module, unless they carry some additional indication by the API author that the class was designed to be subclassed.  However, it does not yet have an opinion as to what that concrete syntax is.


To sum this all up, the core team is rejecting this proposal and requesting a revision to change the concrete syntax to “public open class Foo” instead of “subclassable class Foo".  This approach satisfies the *unwavering* goal of requiring additional thought when publishing a class as public API, makes subclass-ability orthogonal to access control, and (admittedly as a bit of a swift-evolution process hack) asks the community for an in-depth discussion of the secondary points of the proposal: does it make sense to require every member to be marked as “overridable” in order to be overridden by an open subclass outside of the current module?

The core team appreciates that this is a situation where it is impossible to please everyone, while also recognizing that the challenges faced by developers of pure-Swift code are not exactly analogous to those faced by Objective-C developers.  Thank you to the many and diverse opinions and perspectives that have come in as part of this review cycle!


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

More information about the swift-evolution mailing list