Switch statement tuple labels


(Erica Sadun) #1

Was helping a friend with some code and got inspired. I decided to throw this on list to see if there's any traction.

Idea: Introduce optional argument labels to tuples in switch statements

Motivation: Cases can be less readable when pattern matching tuples. Semantically sugared, optional argument labels could increase readability for complex `switch` statements by incorporating roles into cases.

Here's an example before:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, battery.isCalculating) {
    case (.isACPowered, true):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, true):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

and after:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, calculating: battery.isCalculating) {
    case (.isACPowered, calculating: true):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, calculating: true):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

It's a pretty minor change, and I could see it being added to allow case statements to be more readable with a minimal change to the compiler. I also have a back-burnered proposal I intend to introduce in Phase 2 that would introduce Boolean raw value enumerations for flags.

Thoughts?

-- E


(David Sweeris) #2

Was helping a friend with some code and got inspired. I decided to throw this on list to see if there's any traction.

Idea: Introduce optional argument labels to tuples in switch statements

Motivation: Cases can be less readable when pattern matching tuples. Semantically sugared, optional argument labels could increase readability for complex `switch` statements by incorporating roles into cases.

Here's an example before:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, battery.isCalculating) {
    case (.isACPowered, true):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, true):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

and after:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, calculating: battery.isCalculating) {
    case (.isACPowered, calculating: true):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, calculating: true):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

It's a pretty minor change, and I could see it being added to allow case statements to be more readable with a minimal change to the compiler. I also have a back-burnered proposal I intend to introduce in Phase 2 that would introduce Boolean raw value enumerations for flags.

Thoughts?

I can't think of a reason not to do that... +1

- Dave Sweeris

···

Sent from my iPhone

On Jan 1, 2017, at 19:25, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:


(Tony Allevato) #3

The "after" example you posted seems to work already in Swift today. Is
there something I'm missing?

···

On Sun, Jan 1, 2017 at 7:35 PM David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

Sent from my iPhone

On Jan 1, 2017, at 19:25, Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

Was helping a friend with some code and got inspired. I decided to throw
this on list to see if there's any traction.

*Idea*: Introduce optional argument labels to tuples in switch statements

*Motivation*: Cases can be less readable when pattern matching tuples.
Semantically sugared, optional argument labels could increase readability
for complex `switch` statements by incorporating roles into cases.

Here's an example before:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, battery.isCalculating) {
    case (.isACPowered, true):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, true):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

and after:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, *calculating: battery.isCalculating*) {
    case (.isACPowered, *calculating: true*):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, *calculating: true*):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

It's a pretty minor change, and I could see it being added to allow case
statements to be more readable with a minimal change to the compiler. I
also have a back-burnered proposal I intend to introduce in Phase 2 that
would introduce Boolean raw value enumerations for flags.

Thoughts?

I can't think of a reason not to do that... +1

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


(Erica Sadun) #4

Well, it turns out, I was testing it on already established values. The first of the following two examples works but the second does not.

switch (true, y: false) {
case (true, y: false): print("tf")
default: print("nope")
}

let testTuple2 = (true, false)

switch testTuple2 {
// error: tuple pattern element label 'y' must be '_'
case (true, y: false): print("tf")
default: print("nope")
}

I think this gets a 95% Emily Litella (https://en.wikipedia.org/wiki/Emily_Litella). "Nevermind."

And thanks, Tony,

-- E

···

On Jan 1, 2017, at 8:49 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

The "after" example you posted seems to work already in Swift today. Is there something I'm missing?

On Sun, Jan 1, 2017 at 7:35 PM David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPhone

On Jan 1, 2017, at 19:25, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Was helping a friend with some code and got inspired. I decided to throw this on list to see if there's any traction.

Idea: Introduce optional argument labels to tuples in switch statements

Motivation: Cases can be less readable when pattern matching tuples. Semantically sugared, optional argument labels could increase readability for complex `switch` statements by incorporating roles into cases.

Here's an example before:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, battery.isCalculating) {
    case (.isACPowered, true):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, true):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

and after:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, calculating: battery.isCalculating) {
    case (.isACPowered, calculating: true):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, calculating: true):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

It's a pretty minor change, and I could see it being added to allow case statements to be more readable with a minimal change to the compiler. I also have a back-burnered proposal I intend to introduce in Phase 2 that would introduce Boolean raw value enumerations for flags.

Thoughts?

I can't think of a reason not to do that... +1

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


(Derrick Ho) #5

In your first example the switch statement is evaluating whether the given
tuple of type (_:bool, _:bool) matches another tuple of type (_: bool, y:
bool)

The type signature doesn't match.

I believe the change would not be additive like you say because it seems
like it would require a change in how tuple compare with each other.

···

On Mon, Jan 2, 2017 at 12:00 AM Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

Well, it turns out, I was testing it on already established values. The
first of the following two examples works but the second does not.

switch (true, y: false) {
case (true, y: false): print("tf")
default: print("nope")
}

let testTuple2 = (true, false)

switch testTuple2 {
// error: tuple pattern element label 'y' must be '_'
case (true, y: false): print("tf")
default: print("nope")
}

I think this gets a 95% Emily Litella (
https://en.wikipedia.org/wiki/Emily_Litella). "Nevermind."

And thanks, Tony,

-- E

On Jan 1, 2017, at 8:49 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

The "after" example you posted seems to work already in Swift today. Is
there something I'm missing?

On Sun, Jan 1, 2017 at 7:35 PM David Sweeris via swift-evolution < > swift-evolution@swift.org> wrote:

Sent from my iPhone

On Jan 1, 2017, at 19:25, Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

Was helping a friend with some code and got inspired. I decided to throw
this on list to see if there's any traction.

*Idea*: Introduce optional argument labels to tuples in switch statements

*Motivation*: Cases can be less readable when pattern matching tuples.
Semantically sugared, optional argument labels could increase readability
for complex `switch` statements by incorporating roles into cases.

Here's an example before:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, battery.isCalculating) {
    case (.isACPowered, true):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, true):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

and after:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, *calculating: battery.isCalculating*) {
    case (.isACPowered, *calculating: true*):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, *calculating: true*):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

It's a pretty minor change, and I could see it being added to allow case
statements to be more readable with a minimal change to the compiler. I
also have a back-burnered proposal I intend to introduce in Phase 2 that
would introduce Boolean raw value enumerations for flags.

Thoughts?

I can't think of a reason not to do that... +1

- Dave Sweeris
_______________________________________________
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


(Tony Allevato) #6

It looks like it's a bit more complicated than that. I just tried a few
cases in the REPL:

let t1 = (a: 5, b: true)
let t2 = (5, b: true)

// equality testing works
t1 == t2    // works: $R0: Bool = true

// assignment in both directions works (missing label -> has label and has
label -> missing label)
let t3: (Int, b: Bool) = t1    // works: t3: (Int, b: Bool) = { 0 = 5, b =
true }
let t4: (a: Int, b: Bool) = t2    // works: t4: (a: Int, b: Bool) = { a =
5, b = true }

// switch expression has labels, case patterns are missing some:
switch t1 {
case (5, b: true): print("works")
default: print("doesn't work")
}
// works: prints "works"

// switch expression is missing a label, case pattern has all of them:
switch t2 {
case (a: 5, b: true): print("works")
default: print("doesn't work")
}
// error: repl.swift:11:7: error: tuple pattern element label 'a' must be
'_'
// case (a: 5, b: true): print("works")
//       ^

So is this an inconsistency in the way tuples are handled there?

···

On Mon, Jan 2, 2017 at 12:17 AM Derrick Ho <wh1pch81n@gmail.com> wrote:

In your first example the switch statement is evaluating whether the given
tuple of type (_:bool, _:bool) matches another tuple of type (_: bool, y:
bool)

The type signature doesn't match.

I believe the change would not be additive like you say because it seems
like it would require a change in how tuple compare with each other.

On Mon, Jan 2, 2017 at 12:00 AM Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

Well, it turns out, I was testing it on already established values. The
first of the following two examples works but the second does not.

switch (true, y: false) {
case (true, y: false): print("tf")
default: print("nope")
}

let testTuple2 = (true, false)

switch testTuple2 {
// error: tuple pattern element label 'y' must be '_'
case (true, y: false): print("tf")
default: print("nope")
}

I think this gets a 95% Emily Litella (
https://en.wikipedia.org/wiki/Emily_Litella). "Nevermind."

And thanks, Tony,

-- E

On Jan 1, 2017, at 8:49 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

The "after" example you posted seems to work already in Swift today. Is
there something I'm missing?

On Sun, Jan 1, 2017 at 7:35 PM David Sweeris via swift-evolution < > swift-evolution@swift.org> wrote:

Sent from my iPhone

On Jan 1, 2017, at 19:25, Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

Was helping a friend with some code and got inspired. I decided to throw
this on list to see if there's any traction.

*Idea*: Introduce optional argument labels to tuples in switch statements

*Motivation*: Cases can be less readable when pattern matching tuples.
Semantically sugared, optional argument labels could increase readability
for complex `switch` statements by incorporating roles into cases.

Here's an example before:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, battery.isCalculating) {
    case (.isACPowered, true):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, true):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

and after:

fileprivate func chargeState(for battery: BatteryService)
    -> ChargeState
{
    switch (battery.state, *calculating: battery.isCalculating*) {
    case (.isACPowered, *calculating: true*):
        return .calculating(isDischarging: false)
    case (.isACPowered, _) where battery.isCharging:
        return .charging
    case (.isACPowered, _):
        return .acPower
    case (_, *calculating: true*):
        return .calculating(isDischarging: true)
    default:
        return .batteryPower
    }
}

It's a pretty minor change, and I could see it being added to allow case
statements to be more readable with a minimal change to the compiler. I
also have a back-burnered proposal I intend to introduce in Phase 2 that
would introduce Boolean raw value enumerations for flags.

Thoughts?

I can't think of a reason not to do that... +1

- Dave Sweeris
_______________________________________________
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


(Alexis) #7

If the input has labels, including them in the pattern has clear value: the compiler can check that the labels you expected are there, preventing value swapping bugs. Being able to omit the labels in the pattern is a reasonable convenience to avoid repeating yourself over and over. But being able to insert arbitrary labels that don’t match anything from the input is very weird, because it doesn’t really make anything more convenient or give the compiler fuel to catch mistakes.

The proposal is essentially asking for slightly nicer syntax for comments in patterns. That is,

switch (1, 2) {
case (width: 0, height: 0):

}

is entirely equivalent to:

switch (1, 2) {
case (/*width:*/ 0, /*height:*/ 0):

}

That said, there’s a very clear inconsistency here between if-case-let and switch-case-let, which you would expect to be semantically equivalent:

let t1: (Int, Int) = (0, 0)

// This works
if case let (a: 0, b: y5) = t1 {
  print("hello")
}

// This doesn't
switch t1 {
case let (a: 0, b: y6):
    print("hallo")
}

So unless someone has a compelling argument that if-case-let and switch-case-let should be different, one of these should probably be changed. Unless the behaviour of if-case-let is *clearly* a random compiler bug that no one is relying on, then source stability dictates that switch should become more permissive and allow these “comment" labels. I don’t have enough background in this feature’s design to say either way.