Some of my concerns raised here and here have been addressed, but others remain unaddressed.
In short, my primary concern with this proposal is that it will generate many surprising side effects that will be jarring to both new and long-time users. We are implicitly returning values which changes the inferred type.
For a function this is not a big deal, since every func must explicitly declare its return type and it must agree with the implementation's return value.
For an if/switch/do expression, this is a bigger deal since the returned type need not be declared explicitly. So a small change can seem odd.
var childSafety = true
let carBackDoorLabel = switch carBackDoorButtonAction {
case .lock:
"Locked"
case .unlock:
if childSafety { // this is an if expression inside a switch expression.
"Locked"
} else {
"Unlocked"
}
}
print("The lock on the back door of the car is now \(carBackDoorLabel)")
This example is obviously contrived but it shows how easy it is to abuse this. If I wrote the above, and then later needed to add print statements to debug, then I'd get different results based simply on line order.
var childSafety = true
let carBackDoorLabel = switch carBackDoorButtonAction {
case .lock:
print("childSafety: \(childSafety)")
"Locked"
case .unlock:
if childSafety { // this is an if expression inside a switch expression.
"Locked"
} else {
"Unlocked"
}
print("childSafety: \(childSafety)")
}
// đ´ All branches of a switch expression must evaluate to the same type.
Even worse, if I simply change the order in this way, I get no error, but the switch expression evaluates to the type Void
which would be very surprising.
var childSafety = true
let carBackDoorLabel = switch carBackDoorButtonAction {
case .lock:
"Locked"
print("childSafety: \(childSafety)")
case .unlock:
if childSafety { // this is an if expression inside a switch expression.
"Locked"
} else {
"Unlocked"
}
print("childSafety: \(childSafety)")
}
// đ´ All branches of a switch expression must evaluate to the same type.
In most languages, it is not expected that simply adding a print statement can completely change the type, or worse yet, make your code not even compile!
It is also not required to explicitly declare the return type for a closure, so it is not hard to imagine that this same scenario could easily lead to surprising inferred types for closures. Except with closures, the user already has to juggle the cognitive overload of everything else in the closures type definition including the input parameters, capture list, @escaping
, and concurrency isolation.
For those who are in favor of this feature, I also see the potential benefits, but I think there are a lot of new ambiguities and rough edges that it would create that we have yet to discover, let alone mitigate. At the moment, I'm not convinced that the pros outweigh the cons. I would like to be wrong.