Draft Proposal: Declare variables in 'case' labels with multiple patterns


(Andrew Bennett) #1

Hi,

I'd like to discuss declaring variables in case labels with multiple
patterns. I've written a draft proposal, but I'd like to discuss it first
before formally proposing anything.

https://github.com/therealbnut/swift-evolution/blob/a137202e41588b71d3c0511cff85f82ec5f65629/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md

In short:

switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
}

The original proposal is here, it may need to adapt after discussion, so
I'll try to keep the proposal at that link up-to-date.

Thanks,
Andrew

Declare variables in 'case' labels with multiple patterns

   - Proposal: SE-0022
   <https://github.com/therealbnut/swift-evolution/blob/master/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md>
   - Author(s): Andrew Bennett <https://github.com/therealbnut>
   - Status: *In Discussion*
   - Review manager: *Not In Review*

Introduction

In Swift 2, it is possible to match multiple patterns in cases. However
cases cannot contain multiple patterns if the case declares variables.

The following code currently produces an error:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
case .Case1, .Case2:
    break
}

The error is:

`case` labels with multiple patterns cannot declare variables.

This proposal aims to remove this error when each pattern declares the same
variables with the same types.

Motivation

This change reduces repeditive code, and therefore reduces mistakes. It's
consistent with multi-pattern matching when variables aren't defined.
Proposed solution

Allow case labels with multiple patterns to declare patterns by matching
variable names in each pattern.

Using the following enum:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}

These cases should be possible:

case let .Case1(x, _), .Case2(_, x):
case let .Case1(y, x), .Case2(x, y):
case let .Case1(x), .Case2(x):
case .Case1(let x, _), .Case2(_, let x):

Detailed design

Allow case labels with multiple patterns if the case labels match the
following constraints:

   - All patterns declare exactly the same variables.
   - The same variable has the same type in each pattern.

Therefore each pattern is able to produce the same variables for the case
label.
Impact on existing code

This should have no impact on existing code, although it should offer many
opportunities for existing code to be simplified.
Alternatives considered
Using a closure or inline function

Code repitition can be reduced with one pattern per 'case' and handling the
result with an inline function.

func handleCases(value: MyEnum, apply: Int -> Int) -> Int {
    func handleX(x: Int) -> Int {
        return apply(x) + 1
    }
    let out: Int
    switch value {
    case .Case1(let x, 2):
        out = handleX(x)
    case .Case2(2, let x):
        out = handleX(x)
    case .Case1, .Case2:
        out = -1
    }
    return out
}

This syntax is much more verbose, makes control flow more confusing, and
has the limitations of the what the inline function may capture.

In the above example apply cannot be @noescape because handleX captures it.

Also in the above example if out is captured and assigned by handleX then
it must be var, not let. This can produce shorter syntax, but is not as
safe; out may accidentally be assigned more than once, additionally out also
needs to initialized (which may not be possible or desirable).
Extending the fallthrough syntax

A similar reduction in code repetition can be achieved if fallthrough
allowed variables to be mapped onto the next case, for example:

switch test {
    case .Case1(let x, 2):
        fallthrough .Case2(_, x)
    case .Case2(3, .let x):
        print("x: \(x)")
}

This is not as intuitive, is a hack, and fallthrough should probably be
discouraged. It is much more flexible, a programmer could adjust the value
of x before fallthrough. Flexibility increases the chances of programmer
error, perhaps not as much as code-repitition though.


(Chris Lattner) #2

+1.

-Chris

···

On Jan 22, 2016, at 7:39 PM, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I'd like to discuss declaring variables in case labels with multiple patterns. I've written a draft proposal, but I'd like to discuss it first before formally proposing anything.

https://github.com/therealbnut/swift-evolution/blob/a137202e41588b71d3c0511cff85f82ec5f65629/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md


(Joe Groff) #3

This would be great. Other pattern matching languages with ML heritage can do this, the only reason Swift couldn't was time.

-Joe

···

On Jan 22, 2016, at 7:39 PM, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I'd like to discuss declaring variables in case labels with multiple patterns. I've written a draft proposal, but I'd like to discuss it first before formally proposing anything.

https://github.com/therealbnut/swift-evolution/blob/a137202e41588b71d3c0511cff85f82ec5f65629/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md

In short:

switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
}

The original proposal is here, it may need to adapt after discussion, so I'll try to keep the proposal at that link up-to-date.

Thanks,
Andrew

Declare variables in 'case' labels with multiple patterns

Proposal: SE-0022 <https://github.com/therealbnut/swift-evolution/blob/master/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md>
Author(s): Andrew Bennett <https://github.com/therealbnut>
Status: In Discussion
Review manager: Not In Review
Introduction

In Swift 2, it is possible to match multiple patterns in cases. However cases cannot contain multiple patterns if the case declares variables.

The following code currently produces an error:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
case .Case1, .Case2:
    break
}
The error is:

`case` labels with multiple patterns cannot declare variables.
This proposal aims to remove this error when each pattern declares the same variables with the same types.

Motivation

This change reduces repeditive code, and therefore reduces mistakes. It's consistent with multi-pattern matching when variables aren't defined.

Proposed solution

Allow case labels with multiple patterns to declare patterns by matching variable names in each pattern.

Using the following enum:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
These cases should be possible:

case let .Case1(x, _), .Case2(_, x):
case let .Case1(y, x), .Case2(x, y):
case let .Case1(x), .Case2(x):
case .Case1(let x, _), .Case2(_, let x):
Detailed design

Allow case labels with multiple patterns if the case labels match the following constraints:

All patterns declare exactly the same variables.
The same variable has the same type in each pattern.
Therefore each pattern is able to produce the same variables for the case label.

Impact on existing code

This should have no impact on existing code, although it should offer many opportunities for existing code to be simplified.

Alternatives considered

Using a closure or inline function

Code repitition can be reduced with one pattern per 'case' and handling the result with an inline function.

func handleCases(value: MyEnum, apply: Int -> Int) -> Int {
    func handleX(x: Int) -> Int {
        return apply(x) + 1
    }
    let out: Int
    switch value {
    case .Case1(let x, 2):
        out = handleX(x)
    case .Case2(2, let x):
        out = handleX(x)
    case .Case1, .Case2:
        out = -1
    }
    return out
}
This syntax is much more verbose, makes control flow more confusing, and has the limitations of the what the inline function may capture.

In the above example apply cannot be @noescape because handleX captures it.

Also in the above example if out is captured and assigned by handleX then it must be var, not let. This can produce shorter syntax, but is not as safe; out may accidentally be assigned more than once, additionally out also needs to initialized (which may not be possible or desirable).

Extending the fallthrough syntax

A similar reduction in code repetition can be achieved if fallthrough allowed variables to be mapped onto the next case, for example:

switch test {
    case .Case1(let x, 2):
        fallthrough .Case2(_, x)
    case .Case2(3, .let x):
        print("x: \(x)")
}
This is not as intuitive, is a hack, and fallthrough should probably be discouraged. It is much more flexible, a programmer could adjust the value of x before fallthrough. Flexibility increases the chances of programmer error, perhaps not as much as code-repitition though.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jed Lewison) #4

Also +1. I've run into this a couple of times in the past week and the proposal would allow shorter and easier to read code.

···

Sent from my iPhone

On Jan 23, 2016, at 10:09 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

This would be great. Other pattern matching languages with ML heritage can do this, the only reason Swift couldn't was time.

-Joe

On Jan 22, 2016, at 7:39 PM, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I'd like to discuss declaring variables in case labels with multiple patterns. I've written a draft proposal, but I'd like to discuss it first before formally proposing anything.

https://github.com/therealbnut/swift-evolution/blob/a137202e41588b71d3c0511cff85f82ec5f65629/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md

In short:

switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
}

The original proposal is here, it may need to adapt after discussion, so I'll try to keep the proposal at that link up-to-date.

Thanks,
Andrew

Declare variables in 'case' labels with multiple patterns
Proposal: SE-0022
Author(s): Andrew Bennett
Status: In Discussion
Review manager: Not In Review
Introduction

In Swift 2, it is possible to match multiple patterns in cases. However cases cannot contain multiple patterns if the case declares variables.

The following code currently produces an error:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
case .Case1, .Case2:
    break
}
The error is:

`case` labels with multiple patterns cannot declare variables.
This proposal aims to remove this error when each pattern declares the same variables with the same types.

Motivation

This change reduces repeditive code, and therefore reduces mistakes. It's consistent with multi-pattern matching when variables aren't defined.

Proposed solution

Allow case labels with multiple patterns to declare patterns by matching variable names in each pattern.

Using the following enum:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
These cases should be possible:

case let .Case1(x, _), .Case2(_, x):
case let .Case1(y, x), .Case2(x, y):
case let .Case1(x), .Case2(x):
case .Case1(let x, _), .Case2(_, let x):
Detailed design

Allow case labels with multiple patterns if the case labels match the following constraints:

All patterns declare exactly the same variables.
The same variable has the same type in each pattern.
Therefore each pattern is able to produce the same variables for the case label.

Impact on existing code

This should have no impact on existing code, although it should offer many opportunities for existing code to be simplified.

Alternatives considered

Using a closure or inline function

Code repitition can be reduced with one pattern per 'case' and handling the result with an inline function.

func handleCases(value: MyEnum, apply: Int -> Int) -> Int {
    func handleX(x: Int) -> Int {
        return apply(x) + 1
    }
    let out: Int
    switch value {
    case .Case1(let x, 2):
        out = handleX(x)
    case .Case2(2, let x):
        out = handleX(x)
    case .Case1, .Case2:
        out = -1
    }
    return out
}
This syntax is much more verbose, makes control flow more confusing, and has the limitations of the what the inline function may capture.

In the above example apply cannot be @noescape because handleX captures it.

Also in the above example if out is captured and assigned by handleX then it must be var, not let. This can produce shorter syntax, but is not as safe; out may accidentally be assigned more than once, additionally out also needs to initialized (which may not be possible or desirable).

Extending the fallthrough syntax

A similar reduction in code repetition can be achieved if fallthrough allowed variables to be mapped onto the next case, for example:

switch test {
    case .Case1(let x, 2):
        fallthrough .Case2(_, x)
    case .Case2(3, .let x):
        print("x: \(x)")
}
This is not as intuitive, is a hack, and fallthrough should probably be discouraged. It is much more flexible, a programmer could adjust the value of x before fallthrough. Flexibility increases the chances of programmer error, perhaps not as much as code-repitition though.
_______________________________________________
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


(Andrew Bennett) #5

Thanks Chris :slight_smile:

I accidentally linked a specific commit, here's a proper link:

https://github.com/therealbnut/swift-evolution/blob/therealbnut-case-match/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md

I also fixed a few typos.

···

On Sat, Jan 23, 2016 at 5:53 PM, Chris Lattner <clattner@apple.com> wrote:

On Jan 22, 2016, at 7:39 PM, Andrew Bennett via swift-evolution < > swift-evolution@swift.org> wrote:

Hi,

I'd like to discuss declaring variables in case labels with multiple
patterns. I've written a draft proposal, but I'd like to discuss it first
before formally proposing anything.

https://github.com/therealbnut/swift-evolution/blob/a137202e41588b71d3c0511cff85f82ec5f65629/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md

+1.

-Chris


(Jordan Rose) #6

+1 in general, but as a nitpick I think this should be

case let .Case1(x, 2), let .Case2(2, x):

because the patterns really are distinct. (In other words, it seems weird to me that "case let .Case1(x), .Case2(x)" is different from "case .Case2(x), let .Case1(x)".)

Jordan

···

On Jan 22, 2016, at 19:39, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I'd like to discuss declaring variables in case labels with multiple patterns. I've written a draft proposal, but I'd like to discuss it first before formally proposing anything.

https://github.com/therealbnut/swift-evolution/blob/a137202e41588b71d3c0511cff85f82ec5f65629/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md

In short:

switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
}

The original proposal is here, it may need to adapt after discussion, so I'll try to keep the proposal at that link up-to-date.

Thanks,
Andrew

Declare variables in 'case' labels with multiple patterns

Proposal: SE-0022 <https://github.com/therealbnut/swift-evolution/blob/master/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md>
Author(s): Andrew Bennett <https://github.com/therealbnut>
Status: In Discussion
Review manager: Not In Review
Introduction

In Swift 2, it is possible to match multiple patterns in cases. However cases cannot contain multiple patterns if the case declares variables.

The following code currently produces an error:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
case .Case1, .Case2:
    break
}
The error is:

`case` labels with multiple patterns cannot declare variables.
This proposal aims to remove this error when each pattern declares the same variables with the same types.

Motivation

This change reduces repeditive code, and therefore reduces mistakes. It's consistent with multi-pattern matching when variables aren't defined.

Proposed solution

Allow case labels with multiple patterns to declare patterns by matching variable names in each pattern.

Using the following enum:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
These cases should be possible:

case let .Case1(x, _), .Case2(_, x):
case let .Case1(y, x), .Case2(x, y):
case let .Case1(x), .Case2(x):
case .Case1(let x, _), .Case2(_, let x):
Detailed design

Allow case labels with multiple patterns if the case labels match the following constraints:

All patterns declare exactly the same variables.
The same variable has the same type in each pattern.
Therefore each pattern is able to produce the same variables for the case label.

Impact on existing code

This should have no impact on existing code, although it should offer many opportunities for existing code to be simplified.

Alternatives considered

Using a closure or inline function

Code repitition can be reduced with one pattern per 'case' and handling the result with an inline function.

func handleCases(value: MyEnum, apply: Int -> Int) -> Int {
    func handleX(x: Int) -> Int {
        return apply(x) + 1
    }
    let out: Int
    switch value {
    case .Case1(let x, 2):
        out = handleX(x)
    case .Case2(2, let x):
        out = handleX(x)
    case .Case1, .Case2:
        out = -1
    }
    return out
}
This syntax is much more verbose, makes control flow more confusing, and has the limitations of the what the inline function may capture.

In the above example apply cannot be @noescape because handleX captures it.

Also in the above example if out is captured and assigned by handleX then it must be var, not let. This can produce shorter syntax, but is not as safe; out may accidentally be assigned more than once, additionally out also needs to initialized (which may not be possible or desirable).

Extending the fallthrough syntax

A similar reduction in code repetition can be achieved if fallthrough allowed variables to be mapped onto the next case, for example:

switch test {
    case .Case1(let x, 2):
        fallthrough .Case2(_, x)
    case .Case2(3, .let x):
        print("x: \(x)")
}
This is not as intuitive, is a hack, and fallthrough should probably be discouraged. It is much more flexible, a programmer could adjust the value of x before fallthrough. Flexibility increases the chances of programmer error, perhaps not as much as code-repitition though.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Erica Sadun) #7

+1 too

-- E

···

On Jan 23, 2016, at 6:08 PM, Jed Lewison via swift-evolution <swift-evolution@swift.org> wrote:

Also +1. I've run into this a couple of times in the past week and the proposal would allow shorter and easier to read code.

Sent from my iPhone

On Jan 23, 2016, at 10:09 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This would be great. Other pattern matching languages with ML heritage can do this, the only reason Swift couldn't was time.

-Joe

On Jan 22, 2016, at 7:39 PM, Andrew Bennett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi,

I'd like to discuss declaring variables in case labels with multiple patterns. I've written a draft proposal, but I'd like to discuss it first before formally proposing anything.

https://github.com/therealbnut/swift-evolution/blob/a137202e41588b71d3c0511cff85f82ec5f65629/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md

In short:

switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
}

The original proposal is here, it may need to adapt after discussion, so I'll try to keep the proposal at that link up-to-date.

Thanks,
Andrew

Declare variables in 'case' labels with multiple patterns

Proposal: SE-0022 <https://github.com/therealbnut/swift-evolution/blob/master/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md>
Author(s): Andrew Bennett <https://github.com/therealbnut>
Status: In Discussion
Review manager: Not In Review
Introduction

In Swift 2, it is possible to match multiple patterns in cases. However cases cannot contain multiple patterns if the case declares variables.

The following code currently produces an error:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
case .Case1, .Case2:
    break
}
The error is:

`case` labels with multiple patterns cannot declare variables.
This proposal aims to remove this error when each pattern declares the same variables with the same types.

Motivation

This change reduces repeditive code, and therefore reduces mistakes. It's consistent with multi-pattern matching when variables aren't defined.

Proposed solution

Allow case labels with multiple patterns to declare patterns by matching variable names in each pattern.

Using the following enum:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
These cases should be possible:

case let .Case1(x, _), .Case2(_, x):
case let .Case1(y, x), .Case2(x, y):
case let .Case1(x), .Case2(x):
case .Case1(let x, _), .Case2(_, let x):
Detailed design

Allow case labels with multiple patterns if the case labels match the following constraints:

All patterns declare exactly the same variables.
The same variable has the same type in each pattern.
Therefore each pattern is able to produce the same variables for the case label.

Impact on existing code

This should have no impact on existing code, although it should offer many opportunities for existing code to be simplified.

Alternatives considered

Using a closure or inline function

Code repitition can be reduced with one pattern per 'case' and handling the result with an inline function.

func handleCases(value: MyEnum, apply: Int -> Int) -> Int {
    func handleX(x: Int) -> Int {
        return apply(x) + 1
    }
    let out: Int
    switch value {
    case .Case1(let x, 2):
        out = handleX(x)
    case .Case2(2, let x):
        out = handleX(x)
    case .Case1, .Case2:
        out = -1
    }
    return out
}
This syntax is much more verbose, makes control flow more confusing, and has the limitations of the what the inline function may capture.

In the above example apply cannot be @noescape because handleX captures it.

Also in the above example if out is captured and assigned by handleX then it must be var, not let. This can produce shorter syntax, but is not as safe; out may accidentally be assigned more than once, additionally out also needs to initialized (which may not be possible or desirable).

Extending the fallthrough syntax

A similar reduction in code repetition can be achieved if fallthrough allowed variables to be mapped onto the next case, for example:

switch test {
    case .Case1(let x, 2):
        fallthrough .Case2(_, x)
    case .Case2(3, .let x):
        print("x: \(x)")
}
This is not as intuitive, is a hack, and fallthrough should probably be discouraged. It is much more flexible, a programmer could adjust the value of x before fallthrough. Flexibility increases the chances of programmer error, perhaps not as much as code-repitition though.
_______________________________________________
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


(Andrew Bennett) #8

Thanks Jordan,

I wrote a few responses, mostly misinterpreting what you meant, in the end
I agree with you.

It's more consistent to have a let in front of each case label, so it's of
the form:
    case <PATTERN>, <PATTERN>, <PATTERN>, ...:

Each pattern is self contained, individually defining whether it's:
* let .Case1(1,x), or
* .Case1(1, let x)

Yesterday I updated the proposal to state these changes to the existing
Swift grammar:

+ enum-case-pattern-list → enum-case-pattern |+
  enum-case-pattern , enum-case-pattern-list+ pattern →
enum-case-pattern-list- pattern → enum-case-pattern

https://github.com/therealbnut/swift-evolution/blob/therealbnut-case-match/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md

It seems that I accidentally already did what you proposed, because the
grammar was structured like you suggested.

I'll fix the examples in the proposal. It's certainly shorter to only add
`let` once, but it probably complicates the grammar and pairs the let with
the case rather than the pattern (which is inconsistent).

The relevant bits of the current grammar:

    enum-case-pattern *→* type-identifier­ [opt]­ .
­enum-case-name­ tuple-pattern­ [opt]
    tuple-pattern *→* (­tuple-pattern-element-list­ [opt]­)­
    tuple-pattern-element-list *→* tuple-pattern-element­ |
                                 tuple-pattern-element­ ,
­tuple-pattern-element-list
    tuple-pattern-element *→* pattern
    pattern *→* value-binding-pattern
    value-binding-pattern *→* *var­* pattern­ |
* let* ­pattern­

···

On Tue, Jan 26, 2016 at 9:15 AM, Jordan Rose <jordan_rose@apple.com> wrote:

+1 in general, but as a nitpick I think this should be

case let .Case1(x, 2), let .Case2(2, x):

because the patterns really are distinct. (In other words, it seems weird
to me that "case let .Case1(x), .Case2(x)" is different from "case
.Case2(x), let .Case1(x)".)

Jordan

On Jan 22, 2016, at 19:39, Andrew Bennett via swift-evolution < > swift-evolution@swift.org> wrote:

Hi,

I'd like to discuss declaring variables in case labels with multiple
patterns. I've written a draft proposal, but I'd like to discuss it first
before formally proposing anything.

https://github.com/therealbnut/swift-evolution/blob/a137202e41588b71d3c0511cff85f82ec5f65629/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md

In short:

switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
}

The original proposal is here, it may need to adapt after discussion, so
I'll try to keep the proposal at that link up-to-date.

Thanks,
Andrew

Declare variables in 'case' labels with multiple patterns

   - Proposal: SE-0022
   <https://github.com/therealbnut/swift-evolution/blob/master/proposals/0023-declare-variables-in-case-labels-with-multiple-patterns.md>
   - Author(s): Andrew Bennett <https://github.com/therealbnut>
   - Status: *In Discussion*
   - Review manager: *Not In Review*

Introduction

In Swift 2, it is possible to match multiple patterns in cases. However
cases cannot contain multiple patterns if the case declares variables.

The following code currently produces an error:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}
switch value {
case let .Case1(x, 2), .Case2(2, x):
    print(x)
case .Case1, .Case2:
    break
}

The error is:

`case` labels with multiple patterns cannot declare variables.

This proposal aims to remove this error when each pattern declares the
same variables with the same types.

Motivation

This change reduces repeditive code, and therefore reduces mistakes. It's
consistent with multi-pattern matching when variables aren't defined.
Proposed solution

Allow case labels with multiple patterns to declare patterns by matching
variable names in each pattern.

Using the following enum:

enum MyEnum {
    case Case1(Int,Float)
    case Case2(Float,Int)
}

These cases should be possible:

case let .Case1(x, _), .Case2(_, x):
case let .Case1(y, x), .Case2(x, y):
case let .Case1(x), .Case2(x):
case .Case1(let x, _), .Case2(_, let x):

Detailed design

Allow case labels with multiple patterns if the case labels match the
following constraints:

   - All patterns declare exactly the same variables.
   - The same variable has the same type in each pattern.

Therefore each pattern is able to produce the same variables for the case
label.
Impact on existing code

This should have no impact on existing code, although it should offer many
opportunities for existing code to be simplified.
Alternatives considered
Using a closure or inline function

Code repitition can be reduced with one pattern per 'case' and handling
the result with an inline function.

func handleCases(value: MyEnum, apply: Int -> Int) -> Int {
    func handleX(x: Int) -> Int {
        return apply(x) + 1
    }
    let out: Int
    switch value {
    case .Case1(let x, 2):
        out = handleX(x)
    case .Case2(2, let x):
        out = handleX(x)
    case .Case1, .Case2:
        out = -1
    }
    return out
}

This syntax is much more verbose, makes control flow more confusing, and
has the limitations of the what the inline function may capture.

In the above example apply cannot be @noescape because handleX captures
it.

Also in the above example if out is captured and assigned by handleX then
it must be var, not let. This can produce shorter syntax, but is not as
safe; out may accidentally be assigned more than once, additionally out also
needs to initialized (which may not be possible or desirable).
Extending the fallthrough syntax

A similar reduction in code repetition can be achieved if fallthrough
allowed variables to be mapped onto the next case, for example:

switch test {
    case .Case1(let x, 2):
        fallthrough .Case2(_, x)
    case .Case2(3, .let x):
        print("x: \(x)")
}

This is not as intuitive, is a hack, and fallthrough should probably be
discouraged. It is much more flexible, a programmer could adjust the value
of x before fallthrough. Flexibility increases the chances of programmer
error, perhaps not as much as code-repitition though.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution