I'd like to update the Ternary operator to allow it to do for switch what it currently does for if.
Right now we can use:
let x = a ? 10 : 0
as a shorthand for:
let x:Int
if a {
x = 10
} else {
x = 0
}
Right now to set a variable based on the value of an enum (or other pattern) we have to use a switch statement, which can be a bit unwieldy in some cases
let x:Int
switch a {
case .small: x = 10
case .medium: x = 20
case .large: x = 30
default: x = 0
}
I am proposing that we use the Ternary syntax combined with something similar to dictionary syntax to streamline this:
let x = a ? [.small : 10, .medium : 20, .large : 30] : 0
Here the left side of each pair defined is the pattern we are matching against, and the right side is the value which is returned if it matches. The value after the final colon of the ternary represents the default (and matches nicely with the else that it represents for if).
It basically has the same strengths/weaknesses/tradeoffs as using the ternary operator for if. That is, it is very terse and compact, but also takes an extra second to decipher (mainly due to the use of ?)
The other alternative would be to use an equally terse syntax which doesn't use a ? or hanging :
let x = a ~[.small : 10, .medium : 20, .large : 30, _ : 0]
The terseness of ?: works because there are only two values, so it is still comprehensible at a glance. Extending it to cover multiple values wouldn't have that benefit.
The ternary operator barely clears the bar for inclusion in Swift today. From the commonly rejected proposals list:
Replace ?: ternary operator: Definitely magical, but it serves a very important use-case for terse selection of different values. Proposals for alternatives have been intensely discussed, but none have been "better enough" for it to make sense to diverge from the precedent established by the C family of languages.
As such, proposals for further magical sugar of a similar nature, but without the C heritage justification, are probably not a good idea.
Note, you can get a similarly terse lookup syntax with a dictionary literal today:
let x = [.small : 10, .medium : 20, .large : 30][a] ?? 0
this is the kind of thing that makes programming in Swift kind of weird in that if you want to write “performant Swift” you’re basically writing it the way you would spell it in C, i.e. avoiding closures, preferring UnsafeBuffers over Arrays, avoiding String and Character in favor of UnsafeMutableBufferPointer<Unicode.Scalar>, or even UnsafeMutableBufferPointer<UInt8> (!!!). This makes code that can be even harder to read than C because of how Swift represents pointers and buffers, and things like its lack of char literals — you have to type 97 to get the letter 'a'.
meanwhile there’s this weird high level dialect of Swift that uses Array and Dictionary and String that is about as readable, and about as fast as Python.
You shouldn't have to use a different syntactic form or play around with dictionaries to pattern-match in expression contexts. switch and if should just be expressions, IMO, once the type checker problems that would currently make type-checking a switch expression prohibitively expensive are fixed.
Scala uses syntax very similar to that Joe Groff showed. Another possibility would be return instead of yield and for return from a function name.return. EG:
func allNegativeSquares(_ ints: [Int]) -> [Int] {
return for i in ints {
if i > 0 {
allNegativeSquares.return [0] // Named return to indicate return from outer scope.
return i * i
}
}
This has three advantages relative to yield: clarity that a return is non local, unified unnamed return to mean return from inner scope, and consistency with closures. It has the disadvantage of been a breaking change.
Another possibility is yield for all local returns and return for all non-local. Again clarifies what is going on and again is a breaking change.
Sure, I also think making for work that way in Swift would be difficult without inviting confusion, but that's one example of what people have in mind when they talk about for expressions.
I like how you are trying to think of a way to make the expression shorter, but then again there is a cost to introducing new syntax. Especially when it is rather unique.
Here is my attempt at using a ternary operator to match your "unwieldy" switch statement. It works as of swift 4:
// Assuming we have an enum like this
enum Size {
case small, medium, large, other
}
// My alternative to using switch statements
let a: Size = .other
let x = (a == .small) ? 10 : (a == .medium) ? 20 : (a == .large) ? 30 : 0
There is a little bit of repetition even though its shorter then the standard switch statement.
But I am ok with it.
Honestly, I hate using multiple ternary expressions at once, since I am not sure if the result works as intended. I would take a standalone dictionary any time:
let presets: [Size:Int] = [.small: 10, .medium: 20, .large: 30]
let x = presets[a] ?? 0
The only thing I don’t like about this solution is the explicit type signature on the dictionary. Otherwise I think it’s reasonably low-lech (dumb is good!) and perfectly readable.