The great access control battle debate is finally over! Just kidding. Of course, the holy war discussion is ongoing. While SE-0159 was rejected with much dismay, there are final talks on how to remedy the access control situation in Swift before its too late. Friction-driven development wins again! 😉 In other news, a number of new proposals are under review as we marinate in the Swift 4 phase 2 development cycle.
Sponsored Link
iOS Dev Weekly
Dave Verwer from iOS Dev Weekly here. I’m a big fan of the Swift Weekly Brief so when I saw they were looking for support, I thought I should do that! Keep up the good work Jesse!
Swift Unwrapped
Episode 5: Objective-C Interoperability
There’s been a much stronger focus on calling Objective-C from Swift than the other way around, but in episode talk we’ll cover both directions and the parts of the Swift language involved in the process.
News and community
Yours truly wrote a post about the current Swift access control debates and changes, Thoughts on Swift access control: And the opportunity costs of Swift evolution. I provide a brief history and analysis of exactly how we got to this point, and what I think is the best way forward.
JP Simard noticed references to “rename” actions in SourceKit in Xcode 8.3, and predicted we’ll see refactoring support for Swift in Xcode 9. He also pointed out that the “Swift Syntax Structured Editing Library” that was announced a couple of months ago is an even bigger indication of refactoring support. If true, this would be a huge relief for developers. This is something I’ve wanted from Xcode for years (!) now. Let’s hope we see this at WWDC in a few months!
Speaking of JP Simard, he released SourceKitten 0.17.1, which includes Swift 3.1 support.
Greg Heo wrote a fantastic post explaining the reduce(_:_:)
function. His prose is eloquent. His visuals and diagrams are excellent. And of course, no article is complete without a poem:
Filter, reduce, and map:
Collections transformed at a tap.
These functional friends,
your code they will cleanse,
with immutable inputs, no trap.
Commits and pull requests
We’ll skip this section this week as there’s been too much happening on swift-evolution. Ain’t nobody got time for that. 😄
Returned proposals
SE-0161: Smart KeyPaths: Better Key-Value Coding for Swift by David Smith, Michael LeHew, and Joe Groff was reviewed and then returned for a second review.
The proposal:
We propose a family of concrete Key Path types that represent uninvoked references to properties that can be composed to form paths through many values and directly get/set their underlying values.
We Can Do Better than String
On Darwin platforms Swift’s existing#keyPath()
syntax provides a convenient way to safely refer to properties. Unfortunately, once validated, the expression becomes aString
which has a number of important limitations:
- Loss of type information (requiring awkward
Any
APIs)- Unnecessarily slow to parse
- Only applicable to
NSObjects
- Limited to Darwin platforms
Use/Mention Distinctions
While methods can be referred to without invoking them (let x = foo.bar
instead oflet x = foo.bar()
), this is not currently possible for properties and subscripts. Making indirect references to a properties’ concrete types also lets us expose metadata about the property, and in the future additional behaviors.More Expressive KeyPaths
We would also like to support being able to use Key Paths to access into collections, which is not currently possible.
From the revision email:
The proposal was very well-received except that reviewers felt that the
#keyPath
syntax was far too heavy for this new language construct, and preferred the lighter-weight syntax of the pre-review drafts. This proposal is returned for revision to address the syntax.The heavyweight
#keyPath
syntax was requested by the core team after reviewing earlier drafts, which used a far lighter syntax:
The core team’s specific concern was that these key path expressions (e.g.,
Person.friends[0].name
) don’t make it sufficiently clear that the actual property accesses are being delayed, and that the contextual cues (“Person.” vs. “luke.”) are insufficient to disambiguate for the human reader. Hence, the request for a different (more explicit) syntax.Reviewers rightly point out that it is natural for key-paths to use the same syntax as unapplied instance method references, e.g.,
Person.someInstanceMethod
produces a value of some function type with theSelf
type curried, e.g.,(Person) -> (param-types) -> result-type
. The core team agrees with this sentiment. The core team also felt that Swift’s existing unapplied method references suffer from the same clarity problems as the initial key-path syntax, i.e., that it isn’t sufficiently clear that the actual application ofself
is being delayed.
Rejected proposals
SE-0159: Fix Private Access Levels was rejected.
The core team had a lengthy discussion of this proposal as well as related ideas that came up during (and prior to) the review.
SE-0159 specifically sought to revert the main user-facing part of SE-0025, which gave
private
lexical-scoping semantics and introducedfileprivate
. The core team felt that there was sufficient evidence that more-restrictive-than-fileprivate access control is in use within the Swift community and in established patterns, such that it would be harmful to remove the functionality introduced by SE-0025 at this point.The core team discussed the idea of renaming to keywords that was brought up in the thread as a way to address many of the concerns raised in SE-0159 while providing the same language semantics. Specifically:
private
->scoped
fileprivate
->private
The core team determined that such a change, while (technically) easy to automatically migrate, would introduce far too much churn in Swift code bases moving from Swift 3 to Swift 4, compromising the source stability goals set out for Swift 4.
Finally, the core team discussed a different potential design for “private” that admits a limited form of type-based access control within files. We will open a separate discussion thread on Swift Evolution, with the subject “Type-based ‘private’ access within a file”, and are seeking further discussion there and a motivated volunteer to turn it into a new proposal for Swift 4.
Proposals in review
SE-0155: Normalize Enum Case Representation by Daniel Duan and Joe Groff is under review.
In Swift 3, associated values of an enum case are represented by a tuple. This implementation causes inconsistencies in case declaration, construction and pattern matching in several places.
Enums, therefore, can be made more “regular” when we replace tuple as the representation of associated case values. This proposal aims to define the effect of doing so on various parts of the language.
Swift-evolution thread: Normalize Enum Case Representation (rev. 2)
SE-0162: Package Manager Custom Target Layouts by Ankit Aggarwal is under review.
This proposal enhances the
Package.swift
manifest APIs to support custom target layouts, and removes a convention which allowed omission of targets from the manifest.The Package Manager uses a convention system to infer targets structure from disk layout. This works well for most packages, which can easily adopt the conventions, and frees users from needing to update their
Package.swift
file every time they add or remove sources. Adopting the conventions is more difficult for some packages, however – especially existing C libraries or large projects, which would be difficult to reorganize. We intend to give users a way to make such projects into packages without needing to conform to our conventions.
SE-0163: String Revision: Collection Conformance, C Interop, Transcoding by Ben Cohen and Dave Abrahams is under review.
This proposal is to implement a subset of the changes from the Swift 4 String Manifesto.
Specifically:
- Make
String
conform toBidirectionalCollection
- Make
String
conform toRangeReplaceableCollection
- Create a
Substring
type forString.SubSequence
- Create a
Unicode
protocol to allow for generic operations over both types.- Consolidate on a concise set of C interop methods.
- Revise the transcoding infrastructure.
Other existing aspects of
String
remain unchanged for the purposes of this proposal.
SE-0164: Remove final support in protocol extensions by Brian King is under review.
This proposal disallows the
final
keyword when declaring functions in protocol extensions.In the current version of Swift, the
final
keyword does not modify dispatch behavior in any way, and it does not generate an error message. This keyword has no use in Swift’s current protocol model. Functions in protocol extensions cannot be overridden and will always use direct dispatch.
SE-0165: Dictionary & Set Enhancements by Nate Cook is under review.
This proposal comprises a variety of commonly (and less commonly) suggested improvements to the standard library’s
Dictionary
type, from merging initializers to dictionary-specificfilter
andmapValues
methods. The proposed additions toDictionary
, and the corresponding changes toSet
, are detailed in the sections below.
Mailing lists
As mentioned above in the rejection of SE-0159, Doug Gregor has opened a new discussion about implementing type-based ‘private’ access within a file — which will hopefully settle Swift’s access control story for good.
In rejecting SE-0159, the core team described a potential direction we would like to investigate for “private” access control that admits a limited form of type-based access control within files. The core team is seeking some discussion here and a motivated volunteer to put together a proposal along these lines for review in the Swift 4 time-frame (i.e., very soon). To be clear, the core team isn’t sure this is the right direction to go… but it appears promising and we would love to be able to settle the access-control issue.
The design, specifically, is that a “private” member declared within a type “X” or an extension thereof would be accessible from:
- An extension of “X” in the same file
- The definition of “X”, if it occurs in the same file
- A nested type (or extension thereof) of one of the above that occurs in the same file
This design has a number of apparent benefits:
private
becomes the right default for “less than whole module” visibility, and aligns well with Swift coding style that divides a type’s definition into a number of extensions.fileprivate
remains for existing use cases, but now it’s use it more rare, which has several advantages:
- It fits well with the “progressive disclosure” philosophy behind Swift: you can use
public
/internal
/private
for a while before encountering and having to learn aboutfileprivate
(note: we thought this was going to be true of SE-0025, but we were clearly wrong)- When
fileprivate
occurs, it means there’s some interesting coupling between different types in the same file. That makesfileprivate
a useful alert to the reader rather than, potentially, something that we routinely use and overlook so that we can separate implementations into extensions.private
is more closely aligned with other programming languages that use type-based access control, which can help programmers just coming to Swift. When they reach forprivate
, they’re likely to get something similar to what they expect—with a little Swift twist due to Swift’s heavy use of extensions.- Loosening the access restrictions on
private
is unlikely to break existing code.There are likely some drawbacks:
- Developers using patterns that depend on the existing lexically-scoped access control of
private
may find this new interpretation ofprivate
to be insufficiently strict- Swift’s access control would go from “entirely lexical” to “partly lexical and partly type-based”, which can be viewed as being more complicated
Thoughts? Volunteer?
As I mentioned in my blog post, I regret the changes that brought the fileprivate
and open
access controls to Swift and wish we could have considered any changes to this cohesively as part of a Swift theme. However, I believe this compromise the best way — or, least terrible way 😬 — forward given the circumstances and constraints.
There’s a lot of debate on the lists. View points primarily fall into a two categories: (1) don’t do anything and reconsider these changes as part of a broader, holistic redesign of access control and submodules, or (2) implement the Core Team’s suggestion above. Other viewpoints suggest radical renames and redefinitions of the access control keywords, which simply isn’t going to happen because of Swift 4’s source compatibility goals.
The problem with (1) is that it doesn’t seem feasible to defer modifications to access control and address them later, due to churn, potential impact on ABI stability, and the overall roadmap for Swift — there is much more important and impactful work to be done.
From John McCall:
This is why we asked swift-evolution to consider this last tweak: it is realistically the last opportunity to do it.
From Doug Gregor:
The core team felt strongly that we couldn’t change these keywords. Source stability is important to Swift 4, and “don’t break source compatibility so much” is one of the top requests we hear from Swift developers, much more so than requests for specific new language features.
From Xiaodi Wu:
I’m disappointed in this turn of events. While I thought that accepting SE-0159 would have been a better outcome than rejecting it, I am certain that this is an inferior choice to both of those outcomes.
The key problem isn’t principally what this proposed private is. It is that, if adopted, this would be the third flavor of private in as many years. It is a new design with its own kinks (what to do about
private extension
, for example?) and the resultant churn would be most unfortunate.
From Daniel Duan:
I’m concerned that we will have access control changes in a future version yet again, when light-weight modules, or other type of enforced namespace is introduced. Does the core team have any foresight on how this change would interact with such things? I had the same concern for SE-0159 as well.
There’s a implicit drawback to all access control changes that migrator/fix-its cannot fix: we organize our code with tools in the language. Some colleague of mine had came up with schemes that combines file scope and Swift 3
private
to hide details among separate protocol extensions, for example. Code ends up in certain places for a reason and updating access control invalidate those reasons.I hesitate to support any changes until we have some ideas for what “ultimate glory” looks like.
Chris Lattner chimed in, obviously taking a break from designing his yellow self-driving Tesla. 😉
Another way to explain this is as a relaxation of the Swift 3 access control, that would allow private members in a type to also be accessible in extensions to that type, so long as they are in the same file.
While I typically try to avoid chiming in on early discussions like this, I pretty strongly believe that this is a good solution for the reasons you mention […]
From a pragmatic perspective, I feel like this is a great solution that really does solve the problems we have with current access control, all without breaking source compatibility. This is also a major progression from where we are, and doesn’t appear to cut off any future directions (e.g. submodules) since those are cross-file concepts that live between internal/public or between fileprivate/internal.
Just MHO, but I think that the rhetorical attempts to paint this as being similar to “protected” are unsound.
In other discussions, Michael Gottesman replied to a thread on swift-users with some tips on how to narrow down where compiler crashes are happening to help debug. Some pretty helpful tips here!
Finally
And finally — the isn't
operator. Now this is a change I could get behind for Swift 4. 😂