In Swift, "switch" should be an expression not a statement
| Originator: | alexisgallagher | ||
| Number: | rdar://17258614 | Date Originated: | 2014-06-10 |
| Status: | Open | Resolved: | |
| Product: | Swift | Product Version: | beta |
| Classification: | Reproducible: |
It is wonderful that Switch has a switch statement which can be used for rich pattern matching.
## suggestion: switch should be an expression, not a statement
Respectfully, I'd like to suggest that it would also be great if their were a kind of "switch" which was not a statement but an expression that returned a value. You could then use these these switch-expressions to create compact inline value transformers without creating any unnecessary temporary mutable values.
## why this is a good idea
Consider the example from The Swift Programming Language (p 516):
switch self {
case .Ace:
return Values(first: 1, second: 11)
case .Jack, .Queen, .King:
return Values(first: 10, second: nil)
default:
return Values(first: self.toRaw(), second: nil)
}
}
The pattern-match here is lovely but it is awkward when you consider the likely case that you are not always writing this at the end of a function when you return value, and that usually you actually want to do something with the value immediately. Then you have to do:
var temporary:Values! = nil
switch self {
case .Ace:
temporary = Values(first: 1, second: 11)
case .Jack, .Queen, .King:
temporary = Values(first: 10, second: nil)
default:
temporary = Values(first: self.toRaw(), second: nil)
}
}
This works, and now I can use the value in "temporary". But there are many problems: 1. I've had to use an implicitly unwrapped optional just to get hold of my result. 2. The compiler will not use its exhaustiveness check to ensure I've assigned to the optional in all the cases. 3. It's misleading, since I know that the temporary will be constant but now it shows up in code as a variable. 4. For the same reason as 3, it wastes an optimization opportunity.
## possible syntax
Instead, we could let case statements implicitly return their last value, like so:
let result = switch self {
case .Ace:
Values(first: 1, second: 11)
case .Jack, .Queen, .King:
Values(first: 10, second: nil)
default:
Values(first: self.toRaw(), second: nil)
}
Better, yes? Of course! And when type inference can determine the returned type, the syntax is exactly like a switch statement.
But I admit it does raise two questions: how to make this idea work when you need to provide type annotations? and with the existing switch statement more generally?
## syntax to indicate type of the return value
In cases where it is necessary to provide type annotations of the type returned by this switch form, I suggest using a syntax similar to what you've developed for closures:
let result = switch (self:Rank) -> Values {
case .Ace:
Values(first: 1, second: 11)
case .Jack, .Queen, .King:
Values(first: 10, second: nil)
default:
Values(first: self.toRaw(), second: nil)
}
What I like about this syntax is that it looks like this "switch" is an anonymous function definition which transforms its argument expression to its return value -- which is just what it is.
What about the case where people don't care about the return value, and want switch to behave like a statement?
I'm not sure how to handle this, but here are a couple possibilities.
## compatibilty with switch as a statement
### option 1: require explicitly declaring -> Void
One option is that explicitly declaring that the switch statement returns Void is effectively asking it to be treated like a statement, just like before
switch self -> Void {
case .Ace:
doSomethingHere() // irrelevant what I return
case .Jack, .Queen, .King:
doSomethingElse() // irrelevant what I return
default:
doAnotherThing() // irrelevant what I return
}
}
This is bad becomes it changes what people are used to right now. Fortunately, this is not necessary!
### option 2: implicitly return Void when no return type can be inferred
Another option is that a switch statement implicitly defaults to returning Void if the case expressions do not have a common return type:
switch self {
case .Ace:
doSomethingHere() // might return Foo
case .Jack, .Queen, .King:
doSomethingElse() // return Void
default:
doAnotherThing() // might return Bar
}
}
Since there is no type here that can be inferred which works for both Foo, Void, and Bar, the language interprets this as indicating the switch expression should return Void. And now it acts exactly like a normal switch statement!
In other words, I think you could modify the language so that switch behaved like an expression, returning a value, in a way that would not break the existing grammar at all! And the new syntax that would be needed when the return type cannot be inferred, or when someone wanted to provide an additional annotation to the type of the switch expression's argument, is (I think) quite consistent with the syntax the language uses elsewhere for function definition.
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!
Also, http://openradar.appspot.com/17257897