[draft] Compound Names For Enum Cases


(Daniel Duan) #1

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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.


(Douglas Gregor) #2

This looks totally reasonable to me. A couple of comments:

1) Because this proposal is breaking the link between the associated value of an enum case and tuple types, I think it should spell out the rules that switch statements will use when matching an enum value against a a case with an associated value. Some kind of rules fell out of them being treated as tuple types, but they might not be what we want.
2) I wouldn鈥檛 blame you if you wanted to slip in default arguments for associated values here, because this is really making enum cases with associated values much more function-like

聽聽- Doug

路路路

On Jan 19, 2017, at 10:37 AM, Daniel Duan via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Tony Allevato) #3

Obvious +1. Since enum cases with payloads are essentially static factory
functions and you can get references to them just like any other function,
those references should follow the same rules as a regular function.

路路路

On Thu, Jan 19, 2017 at 10:37 AM Daniel Duan via swift-evolution < swift-evolution@swift.org> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum.
Please share you feedback :slight_smile:

(Updating/rendered version:
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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rien) #4

Would this be allowed ?

enum foo {
case bar(num: Int)
case bar(str: String)
case vee(val: Bool)
}

If so, would this still be allowed ?

var a: foo = ...
switch a {
case vee: ...
case bar: ...
}

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl

路路路

On 19 Jan 2017, at 19:37, Daniel Duan via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Daniel Duan) #5

I鈥檝e 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

路路路

# 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:

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.

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:

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:

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:

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:

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:

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:

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
[Daniel Duan]: https://github.com/dduan
[Joe Groff]: 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
[SE Thread]: 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@swift.org> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #6

Is that correct that this proposal will add some sort of overloading enum cases by different labels?

enum Foo {
聽聽聽聽case foo(a: Int)
聽聽聽聽case foo(a: Int, b: Int)
}
Is Foo a valid enum after this proposal?

路路路

--
Adrian Zubarev
Sent with Airmail

Am 19. Januar 2017 um 19:37:50, Daniel Duan via swift-evolution (swift-evolution@swift.org) schrieb:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #7

This looks totally reasonable to me. A couple of comments:

1) Because this proposal is breaking the link between the associated value of an enum case and tuple types, I think it should spell out the rules that switch statements will use when matching an enum value against a a case with an associated value. Some kind of rules fell out of them being treated as tuple types, but they might not be what we want.

I was about to bring up the same. Right now, an enum pattern works like .<identifier> <tuple-pattern>, where the <tuple-pattern> then recursively matches the payload tuple. In this model, it seems like we'd want to treat it more like .<identifier>(<pattern>, <pattern>, ...). Similar to how we lost "tuple splatting" to forward a bunch of arguments, we'd have to decide whether we lose the ability to match all parts of the payload into a tuple. I also don't think we currently enforce matching argument labels, so you can match a `case foo(x: Int, y: Int)` with a `.foo(let q, let z)` or `.foo(apples: let x, bananas: let y)` pattern. We should probably tighten that up as part of this proposal as well.

-Joe

路路路

On Jan 19, 2017, at 1:47 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

2) I wouldn鈥檛 blame you if you wanted to slip in default arguments for associated values here, because this is really making enum cases with associated values much more function-like

聽聽- Doug

On Jan 19, 2017, at 10:37 AM, Daniel Duan via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Daniel Duan) #8

Would this be allowed ?

enum foo {
case bar(num: Int)
case bar(str: String)
case vee(val: Bool)
}

This is certainly an option鈥ersonally I think it's a little unconventional. It's require new syntax for pattern matching, as you demonstrate in the next question. We can hold off this possibility since adding it later wouldn't be a breaking change.

路路路

On Jan 19, 2017, at 11:06 PM, Rien <Rien@Balancingrock.nl> wrote:

If so, would this still be allowed ?

var a: foo = ...
switch a {
case vee: ...
case bar: ...
}

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl

On 19 Jan 2017, at 19:37, Daniel Duan via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #9

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@duan.org> wrote:

I鈥檝e 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

# 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:

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.

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:

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:

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:

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:

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:

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:

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
[Daniel Duan]: https://github.com/dduan
[Joe Groff]: 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
[SE Thread]: 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@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Daniel Duan) #10

This has been answered in various forms in the thread. Short answer: yes.

路路路

On Feb 2, 2017, at 2:05 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

Is that correct that this proposal will add some sort of overloading enum cases by different labels?

enum Foo {
聽聽聽聽case foo(a: Int)
聽聽聽聽case foo(a: Int, b: Int)
}
Is Foo a valid enum after this proposal?

--
Adrian Zubarev
Sent with Airmail

Am 19. Januar 2017 um 19:37:50, Daniel Duan via swift-evolution (swift-evolution@swift.org) schrieb:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #11

Great, thanks. Love it :slight_smile:

+1

路路路

--
Adrian Zubarev
Sent with Airmail

Am 2. Februar 2017 um 14:39:43, Daniel Duan (daniel@duan.org) schrieb:

This has been answered in various forms in the thread. Short answer: yes.

On Feb 2, 2017, at 2:05 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

Is that correct that this proposal will add some sort of overloading enum cases by different labels?

enum Foo {
聽聽聽聽case foo(a: Int)
聽聽聽聽case foo(a: Int, b: Int)
}
Is Foo a valid enum after this proposal?

--
Adrian Zubarev
Sent with Airmail

Am 19. Januar 2017 um 19:37:50, Daniel Duan via swift-evolution (swift-evolution@swift.org) schrieb:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Daniel Duan) #12

This looks totally reasonable to me. A couple of comments:

1) Because this proposal is breaking the link between the associated value of an enum case and tuple types, I think it should spell out the rules that switch statements will use when matching an enum value against a a case with an associated value. Some kind of rules fell out of them being treated as tuple types, but they might not be what we want.

I was about to bring up the same. Right now, an enum pattern works like .<identifier> <tuple-pattern>, where the <tuple-pattern> then recursively matches the payload tuple. In this model, it seems like we'd want to treat it more like .<identifier>(<pattern>, <pattern>, ...). Similar to how we lost "tuple splatting" to forward a bunch of arguments, we'd have to decide whether we lose the ability to match all parts of the payload into a tuple.

I鈥檓 leaning towards 鈥渘o鈥 for simplicity of the language (and implementation). That means this would be source-breaking :disappointed:. Will update the proposal and see how the rest of the feedback goes.

路路路

On Jan 19, 2017, at 2:29 PM, Joe Groff <jgroff@apple.com> wrote:

On Jan 19, 2017, at 1:47 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I also don't think we currently enforce matching argument labels, so you can match a `case foo(x: Int, y: Int)` with a `.foo(let q, let z)` or `.foo(apples: let x, bananas: let y)` pattern. We should probably tighten that up as part of this proposal as well.

-Joe

2) I wouldn鈥檛 blame you if you wanted to slip in default arguments for associated values here, because this is really making enum cases with associated values much more function-like

聽聽- Doug

On Jan 19, 2017, at 10:37 AM, Daniel Duan via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Lattner) #13

This would be great to clean up. Sometime after this happens, we can reconsider nominal value matching (e.g. pattern match against fields within a struct).

-Chris

路路路

On Jan 19, 2017, at 2:29 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 19, 2017, at 1:47 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

This looks totally reasonable to me. A couple of comments:

1) Because this proposal is breaking the link between the associated value of an enum case and tuple types, I think it should spell out the rules that switch statements will use when matching an enum value against a a case with an associated value. Some kind of rules fell out of them being treated as tuple types, but they might not be what we want.

I was about to bring up the same. Right now, an enum pattern works like .<identifier> <tuple-pattern>, where the <tuple-pattern> then recursively matches the payload tuple. In this model, it seems like we'd want to treat it more like .<identifier>(<pattern>, <pattern>, ...). Similar to how we lost "tuple splatting" to forward a bunch of arguments, we'd have to decide whether we lose the ability to match all parts of the payload into a tuple. I also don't think we currently enforce matching argument labels, so you can match a `case foo(x: Int, y: Int)` with a `.foo(let q, let z)` or `.foo(apples: let x, bananas: let y)` pattern. We should probably tighten that up as part of this proposal as well.


(Daniel Duan) #14

Added to the 鈥淪ource 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@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@duan.org <mailto:daniel@duan.org>> wrote:

I鈥檝e 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

# 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:

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.

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:

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:

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:

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:

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:

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:

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
[Daniel Duan]: https://github.com/dduan
[Joe Groff]: 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
[SE Thread]: 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@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #15

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
}

In your example would 'bar(x:)' be required or would a naked 'bar' also be valid? I'm guessing it would not be valid which strikes me as slightly unfortunate. This would create some unpleasant verbosity in some places that isn't required today. (Incidentally, a nontrivial amount of this code would be in easily derivable "isSameCase" equivalence relations that compare the case used but not the associated values)

Another question - if labels become part of the case name does that mean we can "overload" the base name?

enum Foo {
聽聽聽case bar(x: Int)
聽聽聽case bar(y: Int)
}

The example is intentionally problematic because I'm not sure this would be a good idea, but more realistic examples may be possible with cases more meaningfully distinguished by associated value labels.

This is an idea that naturally follows with a move to a more function-like model of enum cases with labels being part of the name so it's worth discussing whether or not it should be allowed.

路路路

Sent from my iPad

On Jan 23, 2017, at 3:32 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

-Joe

On Jan 23, 2017, at 11:38 AM, Daniel Duan <daniel@duan.org> wrote:

I鈥檝e 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

# 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:

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.

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:

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:

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:

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:

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:

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:

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
[Daniel Duan]: https://github.com/dduan
[Joe Groff]: 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
[SE Thread]: 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@swift.org> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dennis Weissmann) #16

I'm also +1 on this, wanted this for a long time :slight_smile:

- Dennis

路路路

On Feb 2, 2017, at 2:42 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Great, thanks. Love it :slight_smile:

+1

--
Adrian Zubarev
Sent with Airmail

Am 2. Februar 2017 um 14:39:43, Daniel Duan (daniel@duan.org <mailto:daniel@duan.org>) schrieb:

This has been answered in various forms in the thread. Short answer: yes.

On Feb 2, 2017, at 2:05 AM, Adrian Zubarev <adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>> wrote:

Is that correct that this proposal will add some sort of overloading enum cases by different labels?

enum Foo {
聽聽聽聽case foo(a: Int)
聽聽聽聽case foo(a: Int, b: Int)
}
Is Foo a valid enum after this proposal?

--
Adrian Zubarev
Sent with Airmail

Am 19. Januar 2017 um 19:37:50, Daniel Duan via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #17

It'd be a good idea to try to find examples of people doing this out in the wild too, to see how widespread it is as well as how onerous the workarounds for losing the feature would be.

-Joe

路路路

On Jan 19, 2017, at 2:58 PM, Daniel Duan <daniel@duan.org> wrote:

On Jan 19, 2017, at 2:29 PM, Joe Groff <jgroff@apple.com> wrote:

On Jan 19, 2017, at 1:47 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

This looks totally reasonable to me. A couple of comments:

1) Because this proposal is breaking the link between the associated value of an enum case and tuple types, I think it should spell out the rules that switch statements will use when matching an enum value against a a case with an associated value. Some kind of rules fell out of them being treated as tuple types, but they might not be what we want.

I was about to bring up the same. Right now, an enum pattern works like .<identifier> <tuple-pattern>, where the <tuple-pattern> then recursively matches the payload tuple. In this model, it seems like we'd want to treat it more like .<identifier>(<pattern>, <pattern>, ...). Similar to how we lost "tuple splatting" to forward a bunch of arguments, we'd have to decide whether we lose the ability to match all parts of the payload into a tuple.

I鈥檓 leaning towards 鈥渘o鈥 for simplicity of the language (and implementation). That means this would be source-breaking :disappointed:. Will update the proposal and see how the rest of the feedback goes.


(Matthew Johnson) #18

This looks totally reasonable to me. A couple of comments:

1) Because this proposal is breaking the link between the associated value of an enum case and tuple types, I think it should spell out the rules that switch statements will use when matching an enum value against a a case with an associated value. Some kind of rules fell out of them being treated as tuple types, but they might not be what we want.

I was about to bring up the same. Right now, an enum pattern works like .<identifier> <tuple-pattern>, where the <tuple-pattern> then recursively matches the payload tuple. In this model, it seems like we'd want to treat it more like .<identifier>(<pattern>, <pattern>, ...). Similar to how we lost "tuple splatting" to forward a bunch of arguments, we'd have to decide whether we lose the ability to match all parts of the payload into a tuple.

I鈥檓 leaning towards 鈥渘o鈥 for simplicity of the language (and implementation). That means this would be source-breaking :disappointed:. Will update the proposal and see how the rest of the feedback goes.

I lean towards 鈥渘o鈥 as well. I like the general direction of departing from treating associated values as tuples. This opens the door to some additional features related to associated values that have been discussed from time to time.

For example, one perennial topic seems to be a desire to treat associated values more like properties (there have been various different suggestions for how to do this). When all cases define an associated value with the same name and type it really can be viewed very much like a property. In other cases, it is possible to view them as read-only, optional properties. I have encountered a number of cases where it has been useful to implement computed properties like this manually and could easily envision a language feature eventually eliminating the boilerplate.

路路路

On Jan 19, 2017, at 4:58 PM, Daniel Duan via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 19, 2017, at 2:29 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:

On Jan 19, 2017, at 1:47 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I also don't think we currently enforce matching argument labels, so you can match a `case foo(x: Int, y: Int)` with a `.foo(let q, let z)` or `.foo(apples: let x, bananas: let y)` pattern. We should probably tighten that up as part of this proposal as well.

-Joe

2) I wouldn鈥檛 blame you if you wanted to slip in default arguments for associated values here, because this is really making enum cases with associated values much more function-like

聽聽- Doug

On Jan 19, 2017, at 10:37 AM, Daniel Duan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

Here鈥檚 a short proposal for fixing an inconsistency in Swift鈥檚 enum. Please share you feedback :slight_smile:

(Updating/rendered version: 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:

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:

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.

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:

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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #19

Sent from my iPad

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
}

In your example would 'bar(x:)' be required or would a naked 'bar' also be valid? I'm guessing it would not be valid which strikes me as slightly unfortunate. This would create some unpleasant verbosity in some places that isn't required today. (Incidentally, a nontrivial amount of this code would be in easily derivable "isSameCase" equivalence relations that compare the case used but not the associated values)

We're not terribly principled about this right now with non-pattern declaration references. You can still reference an unapplied function by its base name alone without its labels, if it's unambiguous:

func foo(x: Int, y: Int) {}

let foo_x_y: (Int, Int) -> () = foo

so it'd be consistent to continue to allow the same in pattern references.

Another question - if labels become part of the case name does that mean we can "overload" the base name?

enum Foo {
聽聽聽case bar(x: Int)
聽聽聽case bar(y: Int)
}

The example is intentionally problematic because I'm not sure this would be a good idea, but more realistic examples may be possible with cases more meaningfully distinguished by associated value labels.

This is an idea that naturally follows with a move to a more function-like model of enum cases with labels being part of the name so it's worth discussing whether or not it should be allowed.

Yeah, if labels really are part of the decl name then this isn't an "overload" at all, so we should allow it.

-Joe

路路路

On Jan 23, 2017, at 3:49 PM, Matthew Johnson <matthew@anandabits.com> wrote:
On Jan 23, 2017, at 3:32 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:


(Matthew Johnson) #20

Sent from my iPad

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
}

In your example would 'bar(x:)' be required or would a naked 'bar' also be valid? I'm guessing it would not be valid which strikes me as slightly unfortunate. This would create some unpleasant verbosity in some places that isn't required today. (Incidentally, a nontrivial amount of this code would be in easily derivable "isSameCase" equivalence relations that compare the case used but not the associated values)

We're not terribly principled about this right now with non-pattern declaration references. You can still reference an unapplied function by its base name alone without its labels, if it's unambiguous:

func foo(x: Int, y: Int) {}

let foo_x_y: (Int, Int) -> () = foo

so it'd be consistent to continue to allow the same in pattern references.

Ok, if we follow this behavior then I am very much +1 on this direction.

Another question - if labels become part of the case name does that mean we can "overload" the base name?

enum Foo {
聽聽聽case bar(x: Int)
聽聽聽case bar(y: Int)
}

The example is intentionally problematic because I'm not sure this would be a good idea, but more realistic examples may be possible with cases more meaningfully distinguished by associated value labels.

This is an idea that naturally follows with a move to a more function-like model of enum cases with labels being part of the name so it's worth discussing whether or not it should be allowed.

Yeah, if labels really are part of the decl name then this isn't an "overload" at all, so we should allow it.

Yeah, that鈥檚 why I put 鈥渙verload鈥 in quotes. :slight_smile:

If this proposal is accepted the compiler will have more flexibility for layout of enums with associated values. Are there any other enum-related features that could impact the layout used (and therefore should be considered before ABI is locked down)?

For example, I鈥檓 thinking of the topic that seems to pop up fairly often related to enums that have several (or possibly all) cases sharing an associated value name and type, which are often viewed as conceptually similar to properties in the discussions that have happened.

路路路

On Jan 23, 2017, at 5:52 PM, Joe Groff <jgroff@apple.com> wrote:

On Jan 23, 2017, at 3:49 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:
On Jan 23, 2017, at 3:32 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

-Joe