[swift-evolution] [draft] Compound Names For Enum Cases

Daniel Duan daniel at duan.org
Mon Jan 23 16:27:36 CST 2017


Added to the “Source Compatibility” section https://github.com/dduan/swift-evolution/blob/compound-names-for-enum-cases/proposals/NNNN-Normalize-Enum-Case-Representation.md

> On Jan 23, 2017, at 1:32 PM, Joe Groff <jgroff at apple.com> wrote:
> 
> This looks pretty good! It might be worth calling out explicitly that matching a payloaded case by name alone still works, e.g.:
> 
> enum Foo { case foo(Int), bar(x: Int) }
> 
> switch Foo.foo(0) {
> case .foo:
>   break
> case .bar(x:):
>   break
> }
> 
> -Joe
> 
>> On Jan 23, 2017, at 11:38 AM, Daniel Duan <daniel at duan.org <mailto:daniel at duan.org>> wrote:
>> 
>> I’ve incorporated feedbacks from this thread.
>> 
>> Rendered: https://github.com/dduan/swift-evolution/blob/compound-names-for-enum-cases/proposals/NNNN-Normalize-Enum-Case-Representation.md <https://github.com/dduan/swift-evolution/blob/compound-names-for-enum-cases/proposals/NNNN-Normalize-Enum-Case-Representation.md>
>> 
>>>> 
>> # Normalize Enum Case Representation
>> 
>> * Proposal: [SE-NNNN][]
>> * Authors: [Daniel Duan][], [Joe Groff][]
>> * Review Manager: TBD
>> * Status: **Awaiting review**
>> 
>> ## Introduction
>> 
>> In Swift 3, associated values for an enum case are represented by
>> a labeled-tuple. This has several undesired effects: inconsistency in enum value
>> construction syntax, many forms of pattern matching, missing features such as
>> specifying default value and missed opportunity for layout improvements.
>> 
>> This proposal aims to make enums more "regular" by replacing tuple as the
>> representation of associated values, making declaration and construction of enum
>> cases more function-like.
>> 
>> Swift-evolution thread: [Compound Names For Enum Cases][SE Thread]
>> 
>> ## Motivation
>> 
>> **Each enum case declares a function that can be used to create a corresponding
>> value. To users who expect these functions to behave "normally", surprises
>> await.**
>> 
>> 1. Associated value labels aren't part of the function name.
>> 
>>     After [SE-0111][] Swift function's fully qualified name consists of its
>>     base-name and all argument labels. As an illustration, one can invoke
>>     a function with its full name:
>> 
>>     ```swift
>>     func f(x: Int, y: Int) {}
>>     f(x: y:)(0, 0) // Okay, this is equivalent to f(x: 0, y: 0)
>>     ```
>> 
>>     This, however, cannot be done when enum cases with associated value were
>>     constructed:
>> 
>>     ```swift
>>     enum Foo {
>>         case bar(x: Int, y: Int)
>>     }
>>     Foo.bar(x: y:)(0, 0) // Does not compile as of Swift 3
>>     ```
>> 
>>     Here, `x` and `y` are labels of bar's payload (a tuple), as opposed to being
>>     part of the case's formal name. This is inconsistent with rest of the
>>     language.
>> 
>> 2. Default value for parameters isn't available in case declarations.
>> 
>>     ```swift
>>     enum Animation {
>>         case fadeIn(duration: TimeInterval = 0.3) // Nope!
>>     }
>>     let anim = Animation.fadeIn() // Would be nice, too bad!
>>     ```
>> 
>> **Associated values being a tuple complicates pattern matching.**
>> 
>> The least unexpected pattern to match a `bar` value is the following:
>> 
>> ```swift
>> if case let .bar(x: p, y: q) = Foo.bar(x: 0, y: 1) {
>>     print(p, q) // 0 1
>> }
>> ```
>> 
>> In Swift 3, there are a few alternatives that may not be obvious to new users.
>> 
>> 1. A pattern with a single value would match and result in a tuple:
>> 
>>     ```swift
>>     if case let .bar(wat) = Foo.bar(x: 0, y: 1) {
>>         print(wat.y) // 1
>>     }
>>     ```
>> 
>> 2. Labels in patterns are not enforced:
>> 
>>     ```swift
>>     // note: there's no label in the following pattern
>>     if case let .bar(p, q) = Foo.bar(x: 0, y: 1) {
>>         print(p, q) // 0 1
>>     }
>>     ```
>> 
>> These complex rules makes pattern matching difficult to teach and to expand to
>> other types.
>> 
>> **Moving away from tuple-as-associated-value also give us opportunity to improve
>> enum's memory layout** since each associated value would no longer play double
>> duty as part of the tuple's memory layout.
>> 
>> ## Proposed Solution
>> 
>> When a enum case has associated values, they will no longer form a tuple. Their
>> labels will become part of the case's declared name. Patterns matching such
>> value must include labels in order matching the declaration.
>> 
>> This proposal also introduce the ability to include a default value for each
>> associated value in the declaration.
>> 
>> ## Detailed Design
>> 
>> ### Make associated value labels part of case's name
>> When labels are present in enum case's payload, they will become part of case's
>> declared name instead of being labels for fields in a tuple.  In details, when
>> constructing an enum value with the case name, label names must either be
>> supplied in the argument list it self, or as part of the full name.
>> 
>> ```swift
>> Foo.bar(x: 0, y: 0) // Okay, the Swift 3 way.
>> Foo.bar(x: y:)(0, 0) // Equivalent to the previous line.
>> Foo.bar(x: y:)(x: 0, y: 0) // This would be an error, however.
>> ```
>> 
>> Note that since the labels aren't part of a tuple, they no longer participate in
>> type checking, similar to functions:
>> 
>> ```swift
>> let f = Foo.bar // f has type (Int, Int) -> Foo
>> f(0, 0) // Okay!
>> f(x: 0, y: 0) // Won't compile.
>> ```
>> 
>> ### Add default value in enum case declarations
>> 
>> From a user's point view, declaring an enum case should remain the same as Swift
>> 3 except now it's possible to add `= expression` after the type of an
>> associated value to convey a default value for that field. Updated syntax:
>> 
>> ```ebnf
>> union-style-enum-case = enum-case-name [enum-case-associated-value-clause];
>> enum-case-associated-value-clause = "(" ")"
>>                                   | "(" enum-case-associated-value-list ")";
>> enum-case-associated-value-list = enum-associated-value-element
>>                                 | enum-associated-value-element ","
>>                                   enum-case-associated-value-list;
>> enum-case-associated-value-element = element-name type-annotation
>>                                      [enum-case-element-default-value-clause]
>>                                    | type [enum-case-element-default-value-clause];
>> element-name = identifier;
>> enum-case-element-default-value-clause = "=" expression;
>> ```
>> 
>> ### Simplify pattern matching rules on enums
>> Syntax for enum case patterns will be the following:
>> 
>> ```ebnf
>> enum-case-pattern = [type-identifier] "." enum-case-name [enum-case-associated-value-pattern];
>> enum-case-associated-value-pattern = "(" [enum-case-associated-value-list-pattern] ")";
>> enum-case-associated-value-list-pattern = enum-case-associated-value-list-pattern-element
>>                                         | enum-case-associated-value-list-pattern-element ","
>>                                           enum-case-associated-value-list-pattern;
>> enum-case-associated-value-list-element = pattern | identifier ":" pattern;
>> ```
>> 
>> … and `case-associated-value-pattern` will be added to the list of various
>> `pattern`s.
>> 
>> Note that `enum-case-associated-value-pattern` is identical to `tuple-pattern`
>> except in names. It is introduced here to denote semantic difference between the
>> two.  Whereas the syntax in Swift 3 allows a single `tuple-pattern-element` to
>> match the entire case payload, the number of
>> `enum-case-associated-value-list-pattern-element`s must be equal to that of
>> associated value of the case in order to be a match. This means this example
>> will be deprecated under this proposal:
>> 
>> ```swift
>> if case let .bar(wat) = Foo.bar(x: 0, y: 1) { // syntax error
>>     // …
>> }
>> ```
>> 
>> Further, `identifier` in `enum-case-associated-value-list-pattern-element` must
>> be the same as the label of corresponding associated value intended for the
>> match. So this will be deprecated as well:
>> 
>> ```swift
>> if case let .bar(p, q) = Foo.bar(x: 0, y: 1) { // missing `x:` and `y:`
>>     // …
>> }
>> ```
>> 
>> ## Source compatibility
>> 
>> As detailed in the previous section, this proposal deprecates certain pattern
>> matching syntax.
>> 
>> Other changes to the syntax are additive and source compatible with Swift 3.
>> 
>> ## Effect on ABI stability and resilience
>> 
>> After this proposal, enum cases may have compound names, which would be mangled
>> differently than Swift 3.
>> 
>> The compiler may also layout enums differently now that payloads are not
>> constrained by having to be part of a tuple.
>> 
>> ## Alternative Considered
>> 
>> To maintain maximum source compatibility, we could introduce a rule that matches
>> all associated values to a labeled tuple. As T.J. Usiyan
>> [pointed out][TJs comment], implementation of the equality protocal would be
>> simplified due to tuple's conformance to `Equatable`. This feature may still be
>> introduced with alternative syntax (perhaps related to splats) later without
>> source-breakage.  And the need to implement `Equatable` may also disappear with
>> auto-devriving for `Equitable` conformance.
>> 
>> A syntax that did stay for source compatibility is allowing `()` in patterns
>> that match enum cases without associated values:
>> 
>> ```swift
>> if let case .x() = Foo.baz { // … }
>> ```
>> 
>> We could remove this syntax as it would make the pattern look more consistent to
>> the case's declaration.
>> 
>> [SE-0111]: https://github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md <https://github.com/apple/swift-evolution/blob/master/proposals/0111-remove-arg-label-type-significance.md>
>> [Daniel Duan]: https://github.com/dduan <https://github.com/dduan>
>> [Joe Groff]: https://github.com/jckarter <https://github.com/jckarter>
>> [SE-NNNN]: NNNN-Normalize-Enum-Case-Representation.md
>> [TJs comment]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030614.html <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030614.html>
>> [SE Thread]: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030477.html <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170116/030477.html>
>> 
>>> On Jan 19, 2017, at 10:37 AM, Daniel Duan via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> Hi all,
>>> 
>>> Here’s a short proposal for fixing an inconsistency in Swift’s enum. Please share you feedback :)
>>> 
>>> (Updating/rendered version: https://github.com/dduan/swift-evolution/blob/compound-names-for-enum-cases/proposals/NNNN-Compound-Names-For-Enum-Cases.md <https://github.com/dduan/swift-evolution/blob/compound-names-for-enum-cases/proposals/NNNN-Compound-Names-For-Enum-Cases.md>)
>>> 
>>> 
>>> ## Introduction
>>> 
>>> Argument labels are part of its function's declaration name. An enum case
>>> declares a function that can be used to construct enum values. For cases with
>>> associated values, their labels should be part of the constructor name, similar
>>> to "normal" function and methods. In Swift 3, however, this is not true. This
>>> proposal aim to change that.
>>> 
>>> ## Motivation
>>> 
>>> After SE-0111, Swift function's fully qualified name consists of its base name
>>> and all argument labels. As a example, one can invoke a function with its
>>> fully name:
>>> 
>>> ```swift
>>> func f(x: Int, y: Int) {}
>>> 
>>> f(x: y:)(0, 0) // Okay, this is equivalent to f(x: 0, y: 0)
>>> ```
>>> 
>>> This, however, is not true when enum cases with associated value were
>>> constructed:
>>> 
>>> ```swift
>>> enum Foo {
>>>     case bar(x: Int, y: Int)
>>> }
>>> 
>>> Foo.bar(x: y:)(0, 0) // Does not compile as of Swift 3
>>> ```
>>> 
>>> Here, the declared name for the case is `foo`; it has a tuple with two labeled
>>> fields as its associated value. `x` and `y` aren't part of the case name. This
>>> inconsistency may surprise some users.
>>> 
>>> Using tuple to implement associated value also limits us from certain layout
>>> optimizations as each payload need to be a tuple first, as opposed to simply be
>>> unique to the enum.
>>> 
>>> ## Proposed solution
>>> 
>>> Include labels in enum case's declaration name. In the last example, `bar`'s
>>> full name would become `bar(x:y:)`, `x` and `y` will no longer be labels in a
>>> tuple. The compiler may also stop using tuple to represent associated values.
>>> 
>>> ## Detailed design
>>> 
>>> When labels are present in enum cases, they are now part of case's declared name
>>> instead of being labels for fields in a tuple. In details, when constructing an
>>> enum value with the case name, label names must either be supplied in the
>>> argument list it self, or as part of the full name.
>>> 
>>> ```swift
>>> Foo.bar(x: 0, y: 0) // Okay, the Swift 3 way.
>>> Foo.bar(x: y:)(0, 0) // Equivalent to the previous line.
>>> Foo.bar(x: y:)(x: 0, y: 0) // This would be an error, however.
>>> ```
>>> 
>>> Note that since the labels aren't part of a tuple, they no longer participate in
>>> type checking, similar to functions:
>>> 
>>> ```swift
>>> let f = Foo.bar // f has type (Int, Int) -> Foo
>>> f(0, 0) // Okay!
>>> f(x: 0, y: 0) // Won't compile.
>>> ```
>>> 
>>> ## Source compatibility
>>> 
>>> Since type-checking rules on labeled tuple is stricter than that on function
>>> argument labels, existing enum value construction by case name remain valid.
>>> This change is source compatible with Swift 3.
>>> 
>>> ## Effect on ABI stability and resilience
>>> 
>>> This change introduces compound names for enum cases, which affects their
>>> declaration's name mangling.
>>> 
>>> The compiler may also choose to change enum payload's representation from tuple.
>>> This may open up more space for improving enum's memory layout.
>>> 
>>> ## Alternatives considered
>>> 
>>> Keep current behaviors, which means we live with the inconsistency.
>>> 
>>> _______________________________________________
>>> 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>
>> 
> 

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


More information about the swift-evolution mailing list