Hello Swift Community,
In late August of last year, a revision of SE-0155 was approved by the core team and the responsibility for its implementation was discharged to me after I responded to a call for implementors. As many of you have noticed, we are now at Swift 4.2 and there is not an implementation of this proposal that has made it into master. I'm writing this to update you all on the current status of the implementation of SE-0155, and to issue reflections on its implementation now that we are nearing the 7-8 month mark without a proper landing.
The Proposal
SE-0155 Normalize Enum Case Representation has as its stated goal a move away from the tuple-based representation for enum case payloads. The rest of the proposal enumerates the features that would effectively be unblocked by this representation change. I list these below along with their current implementation status. For those that are interested, the final branch I intend to land is here. For those that are even more curious, the first patchset I tried to land several months ago is here.
Changing the Representation of Enum Cases
Status: Implemented (and shipping)
Compound Names For Enum Constructors
Status: Implemented
Example
enum Tree<T> {
case empty
indirect case branch(left: Tree<T>, data: T, right: Tree<T>)
}
// Compound function references came for free!
let tree: Tree<Int> = .branch(left:data:right:)(.empty, 42, .empty)
Overloading Enum Cases By Base Name
Status: Implemented
Example
enum SyntaxTree {
case arrow(raw: [TypeVariable])
case arrow(bound: [Type]) // used to be an error in Swift 3
}
Default Parameter Values For Enum Constructors
Status: Not implemented
Example
enum Animation {
case fadeIn(duration: TimeInterval = 0.3)
}
let anim = Animation.fadeIn()
Rationale
The implementation process exposed a number of bugs caused by ancillary representations in the Swift compiler. For example, a class of bugs caused by default argument handling that allows SILGen to form substituted types that are incompatible with their corresponding abstraction patterns was discovered during implementation. Additionally, the work required to support default argument emission would itself require a substantial refactoring to SILGenApply. Attempts to work around this have been stymied by the previously linked bug.
Payload-less Case Declaration
Status: Implemented
Example
enum Tree {
// case leaf() // Banned!
case leaf(Void)
}
Pattern Consistency
Status: Partially Implemented
Rationale
Questions about this part of the proposal were raised before, during, and after the proposal process. The reply from the core team in their original acceptance introduced what I felt at the time was an inconsistency in the pattern matching process. I raised the issue at the time but did not receive an official response - though the discussions I had with members of the Swift community that did respond to the original thread were valuable in their own right. I then implemented and gated both versions of the pattern matching code: one matching Swift's existing behavior but modernized for SE-0155, one matching the behavior of the Core Team's rationale.
Open Questions
The following questions are predicated on the understanding that we will need to revisit this proposal in some way. How deeply we should revisit this is entirely up to the core team. I leave these questions below as a way to begin a discussion about the direction we ultimately want this to go.
Consistency In Pattern Matching
Most pressing is the reality of the specification that exists today. Ungating the pattern matching revision reveals that a rather large amount of code is relying upon the existing Swift 3 pattern matching behavior around tuple element labels and neither the proposal nor the acceptance rationale provide a clear answer for how we wish to resolve this (see the issue thread). If the community still feels that a source breaking change of this magnitude in the name of consistency is warranted, a revision that clearly spells out the rules for pattern matching should be drafted.
Sticking The Landing
As it stands, a large amount of the refactoring required to actually implement the proposal is finished and may be merged at any time. If we can address the core source-breaking components of this proposal, the rest can simply be landed piecemeal with diagnostic gates on the parts that will be implemented later. For example, I currently have default arguments gated pending landing a SILGenApply patch, and the pattern matching consistency code simply needs to be re-gated pending a community discussion.