[swift-evolution] ternary operator ?: suggestion

Paul Ossenbruggen possen at gmail.com
Wed Jan 6 16:22:24 CST 2016


Just to try out some other examples from the Swift book as you can see it gets rid of a lot of boiler plate and duplicated code for almost all of the examples. I think it is just as readable if not more readable and very Swift like. It also leaves more room on the line so you can avoid multiline cases more often. In the book the examples often use “var” when “let” would be preferred. 

This:

switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    print("\(someCharacter) is a consonant")
default:
    print("\(someCharacter) is not a vowel or a consonant")
}

Becomes:

let resultString = someCharacter ?( 
   "a", "e", "i", "o", “u”: "\(someCharacter) is a vowel"
  "b", "c", "d", "f", "g", "h", "j", "k", "l", “m”, "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z”:
	 "\(someCharacter) is a consonant"
  “_”: "\(someCharacter) is not a vowel or a consonant"
)
print(resultString)

This:

let approximateCount = 62
let countedThings = "moons orbiting Saturn"
var naturalCount: String
switch approximateCount {
case 0:
    naturalCount = "no"
case 1..<5:
    naturalCount = "a few"
case 5..<12:
    naturalCount = "several"
case 12..<100:
    naturalCount = "dozens of"
case 100..<1000:
    naturalCount = "hundreds of"
default:
    naturalCount = "many"
}

Becomes:

let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount = approximateCount ?(
	0: 		"no"
	1..<5: 		"a few"
	5..<12: 	"several"
	12..<100:	"dozens of"
	100..<1000: 	"hundreds of"
    	_: 		"many"
)
print("There are \(naturalCount) \(countedThings).")

This:

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
    print("on the x-axis with an x value of \(x)")
case (0, let y):
    print("on the y-axis with a y value of \(y)")
case let (x, y):
    print("somewhere else at (\(x), \(y))")
}

Becomes:

let anotherPoint = (2, 0)
let resultString = anotherPoint ?(
    (let x, 0):   "on the x-axis with an x value of \(x)"
    (0, let y):   "on the y-axis with a y value of \(y)"
    let (x, y):   "somewhere else at (\(x), \(y))"
)
print(resultString)

This:

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}

Becomes:

let yetAnotherPoint = (1, -1)
let resultString = yetAnotherPoint ?(
    let (x, y) where x == y:  "(\(x), \(y)) is on the line x == y"
    let (x, y) where x == -y: "(\(x), \(y)) is on the line x == -y"
    let (x, y):		      "(\(x), \(y)) is just some arbitrary point"
)
print(resultString)

This:

let numberSymbol: Character = "三"  // Simplified Chinese for the number 3
var possibleIntegerValue: Int?
switch numberSymbol {
case "1", "١", "一", "๑":
    possibleIntegerValue = 1
case "2", "٢", "二", "๒":
    possibleIntegerValue = 2
case "3", "٣", "三", "๓":
    possibleIntegerValue = 3
case "4", "٤", "四", "๔":
    possibleIntegerValue = 4
default:
    break
}
if let integerValue = possibleIntegerValue {
    print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
    print("An integer value could not be found for \(numberSymbol).")
}
// prints "The integer value of 三 is 3.”

Becomes:

let numberSymbol: Character = "三"  // Simplified Chinese for the number 3
let possibleIntegerValue:Int? = numberSymbol ?(
    "1", "١", "一", "๑": 1
    "2", "٢", "二", "๒": 2
    "3", "٣", "三", "๓": 3
    "4", "٤", "四", "๔": 4
    _ : nil
)
if let integerValue = possibleIntegerValue {
    print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
    print("An integer value could not be found for \(numberSymbol).")
}
// prints "The integer value of 三 is 3.”

This last one does not automatically make it an optional, you need to manually put the optional type in, just as ternary does. 

The falltrhough examples don’t make sense because you can’t have fallthrough, break or continue statements in expressions, since they single expressions. If you need that, then use a the statement form. 

- Paul

> On Jan 6, 2016, at 11:03 AM, Paul Ossenbruggen <possen at gmail.com> wrote:
> 
> To point 1: I agree it needs a new name, I came up with the “demux expression” but maybe there is a better name. 
> 
> To point 2: Taking a more complex example which uses _ adapted from the swift book. The original 
> 
> switch somePoint {
> case (0, 0):
>     print("(0, 0) is at the origin")
> case (_, 0):
>     print("(\(somePoint.0), 0) is on the x-axis")
> case (0, _):
>     print("(0, \(somePoint.1)) is on the y-axis")
> case (-2...2, -2...2):
>     print("(\(somePoint.0), \(somePoint.1)) is inside the box")
> default:
>     print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
> }
> 
> with the proposed format so the underscore does kind of act like a default in the existing switch statement: 
> 
> let string = somePoint ?
>     (0, 0): 		"(0, 0) is at the origin"
>     (_, 0):		"(\(somePoint.0), 0) is on the x-axis"
>     (0, _):		"(0, \(somePoint.1)) is on the y-axis"
>     (-2...2, -2...2):   "(\(somePoint.0), \(somePoint.1)) is inside the box"
>     (_, _):		"(\(somePoint.0), \(somePoint.1)) is outside of the box"
> 
> So, it kind of used like a default. Note, I am suggesting that this also work, if it seems too loose, this would help add structure in more complex cases. 
> 
> let string = somePoint ?
>     case (0, 0): 		"(0, 0) is at the origin"
>     case (_, 0):		"(\(somePoint.0), 0) is on the x-axis"
>     case (0, _):		"(0, \(somePoint.1)) is on the y-axis"
>     case (-2...2, -2...2):   	"(\(somePoint.0), \(somePoint.1)) is inside the box"
>     default:			"(\(somePoint.0), \(somePoint.1)) is outside of the box"
> 
> So you could still do as you suggest: 
> 
> let string = somePoint ?
>     (0, 0): 		"(0, 0) is at the origin"
>     (_, 0):		"(\(somePoint.0), 0) is on the x-axis"
>     (0, _):		"(0, \(somePoint.1)) is on the y-axis"
>     (-2...2, -2...2):   "(\(somePoint.0), \(somePoint.1)) is inside the box"
>     default:		"(\(somePoint.0), \(somePoint.1)) is outside of the box"
> 
> Mathew says that he thinks that parenthesis should not be required, but I think it seems a little loose without them. It is similar to how control structures have { } around things and I am worried about creating ambiguity. 
> 
> let string = somePoint ?(
>     (0, 0): 		"(0, 0) is at the origin"
>     (_, 0):		"(\(somePoint.0), 0) is on the x-axis"
>     (0, _):		"(0, \(somePoint.1)) is on the y-axis"
>     (-2...2, -2...2):   "(\(somePoint.0), \(somePoint.1)) is inside the box"
>     (_, _):		"(\(somePoint.0), \(somePoint.1)) is outside of the box"
> }
> 
> I don’t think the “case” and “default" is as necessary with the parenthesis around it. 
> 
> Arg! I still want to put the control value inside though! It just kind of floats out there otherwise. 
> 
> let string = ?(somePoint,
>     (0, 0): 		"(0, 0) is at the origin"
>     (_, 0):		"(\(somePoint.0), 0) is on the x-axis"
>     (0, _):		"(0, \(somePoint.1)) is on the y-axis"
>     (-2...2, -2...2):   "(\(somePoint.0), \(somePoint.1)) is inside the box"
>     (_, _):		"(\(somePoint.0), \(somePoint.1)) is outside of the box"
> }
> 
> To point 3: That the ternary presents confusion by using the colon. I think that confusion drops away with the _ as the default. The rule would be that switch expressions would always be exhaustively specified or must use default. For the boolean and index forms the colon does not create confusion, they are used to return the case that does not match one of the choices. 
> 
> let fb = pickOne ? “A", "B", "C", "D", "E", "F", "G” : "Z"
> let fe = truthy == truth ? “unlikely” : “likely”
> 
> or:
> 
> let fb = pickOne ? “A", "B", "C", "D", "E", "F", "G” default:"Z"
> let fe = truthy == truth ? “unlikely” default: “likely”
> 
> or maybe:
> 
> let fb = pickOne ? “A", "B", "C", "D", "E", "F", "G” else:"Z"
> let fe = truthy == truth ? “unlikely” else: “likely”
> 
> Not necessarily recommending that last one but trying it out. Since boolean “ifs" use “else" rather than default. 
> 


> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160106/9efa3179/attachment.html>


More information about the swift-evolution mailing list