Paleogene Therapy: What’s up in Swift Evolution


SE-0091: Improving operator requirements in protocols is accepted with minor revision. Team writes:

The second iteration of this proposal has been very well received by both the community and core team.  The core team requests one minor modification: in an effort to reduce the scope of the proposal, it should specifically require that operator declarations in classes be written as static (or equivalently, as “final class”).  In the future, support for operators may be extended to support dynamic dispatch, and the core team wants to keep the design space open.  The core team also observed that the impact on the standard library is not captured in this proposal, but that can be incorporated later (as an amendment to this proposal) since it should have little user impact.

SE-0107: UnsafeRawPointer API is accepted. Team writes:

The community and core team agree that this proposal clarifies the rules around the unsafe pointer APIs, while making them “work by default” and balance safety and unsafety in an understandable way. Andy has revised the proposal several times due to community feedback (which is why we extended the review period), which makes my job easier because the core team agrees to accept it nearly as-is :-). The only request from the core team is to remove the default value from the ‘alignedTo:’ parameters of the allocate and deallocate methods, forcing their callers to be explicit about the alignment required by an allocation.

SE-0118: Closure Parameter Names and Labels is accepted with (slight) modification. Team writes:

The proposal has been very well received by the community and core team.  The core team agrees with community contributions that “ManagedBuffer<Header,Element>.create(minimumCapacity: 10, makingValueWith: makeHeader)” should be renamed to “makingHeaderWith:”.  The core team extensively discussed the name of “lines.split(whereSeparator: isAllWhitespace)” and agreed to keep the name as proposed, because the alternative name “lines.split(where: isAllWhitespace)” could be confusing given the behavior of dropping the “separator” from the result.

SE-0086: Drop NS Prefix in Swift Foundation is accepted. Team writes:

This proposal has evolved greatly from a single bullet item in the original proposal for improving the translation of Objective-C APIs (SE-0005: Better Translation of Objective-C APIs Into Swift). Discussion spawned the transformative work on Foundation value types (SE-0069: Mutability and Foundation Value Types), and other improvements to the mapping of Objective-C APIs into Swift (e.g., SE-0033: Import Objective-C Constants as Swift TypesSE-0044: Import as MemberSE-0112: Improved NSError Bridging) have informed and improved this proposal.
The community and core team are overall positive on the removal of the Boolean protocol, under the rationale that it is not pulling its weight and its name is confusing next to Bool.  Several members of the core team and a member of the community points out that the *functionality* provided by the Boolean protocol could make sense for Swift if a well-considered design was available, but the core team feels that we should remove Boolean for Swift 3, and consider adding back a replacement when and if a compelling use-case presents itself to motivate that work.
The community and core team agree that this proposal is a huge step forward that enriches the experience working with and extending the Cocoa NSError model in Swift. The core team requests one minor renaming of “attemptRecovery(optionIndex:andThen:)” to “attemptRecovery(optionIndex:resultHandler:)”. It also discussed renaming CustomNSError and RecoverableError, but decided to stay with those names.
The community discussion and core team unanimously agree that this is the right thing to do.
The community and core team agree that this proposal is a better set of names for these protocols.  The core team agrees with the community sentiment that “By” is better than “As” in the protocol names, and has accepted the proposal with this revision.

Returned for Revision

SE-0117: Default classes to be non-subclassable publicly is returned for revision. Team writes,

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!

Team Updates

Update on SE-0111: Remove type system significance of function argument labels. Team writes:

Shortly after SE-0111 was accepted last week, several people newly noticed the proposal and started a discussion about how it appears to be a regression for closure parameters (e.g. callbacks) that could formerly carry labels, but are now not allowed to. These folks observed that it would be more expressive (and consistent with the rest of Swift) to allow parameter labels in function types, because the invocation site of a closure “should” be required to provide those labels. The core team has been following the discussion, agrees that this is a concern, and wants to update the community with a path forward.

The reality of the situation is that the current implementation of parameter labels in function types is inherently broken. Specifically, as one example, there is an implicit conversion from “(a: Int) -> Int” to “(Int) -> Int”. However, there is also an implicit conversion from “(Int) -> Int” to “(b : Int) -> Int”. This means that the compiler currently allows converting from “(a: Int) -> Int” to “(b: Int) -> Int”, which doesn’t make sense, introduces surprising behavior, introduces complexity into the compiler implementation, and is generally a problem. We do have one specific hack to prevent conversion of (e.g.) “(a : Int, b : Int) -> Void” to “(b : Int, a : Int) -> Void”, but this only triggers in specific cases. There are other more complex cases as well, e.g. when using generics “T<(a : Int)->Int>” cannot be considered compatible with “T<(b : Int)->Int>”.

These problems are what initially motivated SE-0111. However, given the feedback, the core team went back to the drawing board to determine whether: a) SE-0111 by itself is the right long term answer, b) whether there were alternate models that could solve the same problems in a different way, or c) whether SE-0111 was the right first step to “ultimate glory” in the field of closure parameter labels. After a long discussion, and many alternatives considered, the core team believes in c), that SE-0111 (with a minor modification) is the right step for Swift 3, because it paves the way for the right model over the long term.

The specific revision requested by the core team to SE-0111 is that all “cosmetic” labels should be required to include an API name of _. For example, this would not be allowed:

var op : (lhs : Int, rhs : Int) -> Int

instead, it should be spelled as:

var op : (_ lhs : Int, _ rhs : Int) -> Int

With this change, we believe that we have paved the way for a purely additive proposal (and thus, post-Swift 3) that will restore the expressive capability of closures with parameter labels.

Here is a sketch of how that would work, in two steps:
First, we extend declaration names for variables, properties, and parameters to allow *parameter names* as part of their declaration name. For example:

var op(lhs:,rhs:) : (Int, Int) -> Int // variable or property.
x = op(lhs: 1, rhs: 2) // use of the variable or property.

// API name of parameter is “opToUse”, internal name is "op(lhs:,rhs:)”.
func foo(opToUse op(lhs:,rhs:) : (Int, Int) -> Int) {
    x = op(lhs: 1, rhs: 2) // use of the parameter

foo(opToUse: +) // call of the function

This will restore the ability to express the idea of a closure parameter that carries labels as part of its declaration, without requiring parameter labels to be part of the type system (allowing, e.g. the operator + to be passed into something that requires parameter labels).
Second, extend the rules for function types to allow parameter API labels *if and only if* they are used as the type of a declaration that allows parameter labels, and interpret them as a sugar form for providing those labels on the underlying declaration. This means that the example above could be spelled as:

var op : (lhs: Int, rhs: Int) -> Int // Nice declaration syntax
x = op(lhs: 1, rhs: 2) // Same as above

// API name of parameter is “opToUse”, internal name is "op(lhs:,rhs:)”.
func foo(opToUse op : (lhs: Int, rhs: Int) -> Int) {
    x = op(lhs: 1, rhs: 2) // Same as above.

foo(opToUse: +) // Same as above.

These two steps will provide the simple and expressive design approach that we have now, without all of the problems that representing parameter labels in the type system introduces. The core team believes that the temporary regression in expressiveness is an acceptable loss for Swift 3, particularly given that this will have no impact on Cocoa or the standard library. In the case of Cocoa, recall that C and Objective-C don’t have parameter labels on their corresponding concepts (Blocks and C function pointers), and the higher order functions in the standard library should not require parameter labels either.

-Chris & the Core Team

 In Review

Comments are closed.