How is SE-0255 (implicit returns) not a violation of Swift's design principles?

How is SE-0255 and the Implicit Return behavior of closures not a violation of Swift's design principles?

In Commonly Rejected Proposals, Implicit Returns are mentioned explicitly as favoring "terseness over readability". Implicit Returns eliminate needing to type 6 characters, at the expense of anyone but the person who wrote said code, being able to gleen over it quickly and derive meaning.

There is a huge difference between:

fooObj
    .someMethodThatMightReturn { isThisAnExpressionOrAReturn() }
    // nobody knows 
    .someOtherMethodThatMightReturn { isThisOneAnExpressionOrAReturn() }
    // nobody knows 
    .yetAnotherMethodThatMightReturn { ohJeeze() }

And this:

fooObj
    .obviouslyJustDoingWorkAndSideEffects { barObject.someMethod() }
    .obviouslyThisReturnsSomething { return isThisOneAnExpressionOrAReturn() }

The less places I have a bold red return (or whatever format you use), the longer it takes me to review someone else's code. This feature saves 3 seconds at the time of writing code at the expense of 20 seconds thinking when it is time to read it.

Am I the only one somewhat horrified to see this practice in Swift?

4 Likes

The examples that you've given here are trailing closures, which have always supported dropping the return for single expressions; these were not changed by SE-0255. That proposal made other single-expression functions consistent with that behavior.

Regarding SE-0255, in the context of a property getter or a function that returns non-Void, it's unambiguous that a single-expression body of that block, even if not preceded by return, must be returning a value of the same type as declared by the property/function and can't simply be calling it for its side effects.

The section about implicit returns that you link to is specifically in reference to omitting them from the body of guard statements, and isn't referring to just omitting the return keyword but omitting the entire return statement (which was been pitched a few times on the forum). The reasons for doing so aren't simply for terseness; it's because there are many possible ways to exit from a guard body: you can break, continue, call something that returns Never (like fatalError), and even in the case where you do return, depending on the return type of the function, you may need to return a non-Void value.

18 Likes

FWIW, your examples were valid before SE-0255 as they're in closures which have ~always allowed omitting return for single expressions.

Readability is subjective and the importance of an indicator depends on the context. E.g. the case Commonly Rejected Proposals mentions is omitting return in guard statements. This is a very different context than something like var fullName { "\(firstName) \(secondName)" }.

IMO whether you're comfortable with this type of change has more to do with the languages you're used to. There are languages that are more terse than Swift, and languages that are less terse than Swift. If you're coming from a more verbose language, you will struggle with the terseness. If you're coming from a more terse language, you will struggle with all the extra syntax.

What I would like to see around this is an improvement in the tooling to visibly indicate when a single expression is being returned to more easily parse things when skimming through code.

2 Likes

Agreed, this is something that Kotlin and the JetBrains IDEs do quite well in my opinion.

Also worth noting that you can't call methods on Void, so each of these closures must be returning a value.

2 Likes

Thanks for the input guys.

I hadn't really put too much thought into how much contextual syntax highlighting would help with that.

I completely and totally agree with you; however, the new trend for the language is to remove as much as possible so nobody has to type anything the argument is "Kotlin can do it! and my eyes hurt looking at it."

Glade to know I'm not the only one thinking that trying to replicate Kotlin, which is broken (no error handling…) and inconsistent in many ways (optional unboxing and cast inference are a mess) is not the best idea for Swift.

Maybe we should explicitly say in the Evolution Guideline that "X can do it" is not a valid argument to push a proposal.

1 Like

That was not the argument presented in SE-0255. As others have noted, the change made Swift more consistent with itself. No other justification was needed.

1 Like

Our mind work like a dog. It train to see return keyword and understand that there is a return. It makes developing faster. With implicit return developer must take into account that it can be return without return word. It's so inconvenience!

1 Like