I appreciate your post but it demonstrates once again the mistake of imagining the solution to a problem that does not exist within the context of EXTREMELY short examples. Which is understandable, because it is (1) difficult to imagine large things and (2) nobody has time to type out real world-examples.
That is true, but the opposition participants aren't confusing verbosity and clarity. We really do mean clarity here.
The following example is NOT clear when return
is elided. It is difficult to see, because we're looking at an extremely short example, it is the equivalent of eliding await
. Both really are just ceremony as you put it. (Eliding await would be less messy in terms of control flow you just lose your signal to the code reader nothing more)
To better exemplify the issue lets lengthen this a bit to something akin to real world.
//In another library (no idea if this is discardable or not)
@discardableResult someBool(flip: Bool) -> Bool {return !flip}
var doSomething: Bool = false
func hasSupplementaryViewAfter() -> Bool {
if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
} else if thisThat {
someBool(flip: false)
} else {
if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
} else {
if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
} else {
//doSomething = someBool(flip: false)
someBool(flip: true)
}
}
}
} else if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
return someBool(flip: false)
} else {
if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
} else {
if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
} else {
if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
} else {
//return here? Okay. I will add return because this is the value, I think they want.
return someBool(flip: true)
}
}
}
}
// if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
// elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
// } else {
// if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
// elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
// } else {
// if let cellIndex = elements.firstIndex(where: { $0.elementCategory == .cell }) {
// elements[cellIndex...].contains { $0.elementCategory == .supplementaryView }
// } else {
// someBool(flip: true)
// }
// }
// }
// return someBool(flip: false)
// FIX ME: we should possibly return true here.
}
This function needs to be fixed. There seem to be multiple ways that we can go about fixing it.
We have an if-statement that can now be thought of as an expression that is the last line of our function. Someone commented out my return line -- accidentally?--, it was just compiling but I added a 'return' statement. I might not ever notice it. The original author who wrote this did or did not intend for someBool function to be the return value always. I'm reading through it to discover intent, but because of where it is placed, and how it is written I don't know. Maybe I just remove return someBool(flip: false)
This is nothing compared to what we witness in a simple one line closure. It is so clear, obvious what will be returned in a one line expression. It is CLEAR in that context.
We require 'await' because it communicates something to the author and those reading their code. It signals how everything is getting executed. Requiring return
(and other various keywords) that yield a value, helps us understand when things are intended to exit ESPECIALLY in large functions. This is not an issue that we'd ever have with single line closures/functions.
I am certain that someone could come up with a better example and debug cycle that better demonstrates the issue.
In one line functions, yes (not quite but we already have this as a feature so it matters not, but yes enough). The ending return
in multi-line functions (that are not necessarily going to be 5 lines, more like 100) helps readers (not the compiler) immediately know intent. And when removed, the compiler can point you to the issue, so you can be sure that you are returning what you intend (safely with out any assumptions) and future readers of your code and those evolving your code can recognize the same and see clearly where you may be returning early. It is also common to re-arrange code. Accidentally moving what was otherwise an elided return
towards the top would neither return nor be alerted to. It might not even be noticed if it was an @discardableResult. This would not happen in a single line function/closure as there is little to distract you. In your example, albeit short, I might not even notice it was returning it is so long.
This is what we mean by 'clarity' and 'safety'. We don't just mean "more or less verbose".