SE-0255: Implicit Returns from Single-Expression Functions

Hi Swift Community,

The review of SE-0255: Implicit Returns from Single-Expression Functions begins now and runs through April 11, 2019.

Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to me as the review manager via email or direct message on the forums. If you send me email, please put "SE-0255" somewhere in the subject line.

What goes into a review of a proposal?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.

When reviewing a proposal, here are some questions to consider:

  • What is your evaluation of the proposal?

  • Is the problem being addressed significant enough to warrant a change to Swift?

  • Does this proposal fit well with the feel and direction of Swift?

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Thank you for contributing to Swift!

Ben Cohen
Review Manager

16 Likes
  • What is your evaluation of the proposal?
    I'm not necessarily in favor of extending this feature to arbitrary functions, but I
    am absolutely for adding it to computed properties.
  • Is the problem being addressed significant enough to warrant a change to Swift?
    It is for computed properties. I'm unsure about functions.
  • Does this proposal fit well with the feel and direction of Swift?
    See above.
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
    Yes, I've experienced this in Javascript and while it does largely work for that language, I feel like most of this features use cases are covered by having this for closures, and most of the rest by extending it to computed properties.
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
    I read the proposal and the pitch thread, and have read some of the previous discussion of this feature.
3 Likes

This solves a long-standing inconsistency between the behavior of expressions used in conjunction with closures, and that of functions and computed property getters. That inconsistency often left me feeling uncertain, and I’m sure many others feel the same. I’m in favor.

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes. Omitting return is not so important, but consistency in behavior is. Inconsistent behavior creates cognitive load.

  • Does this proposal fit well with the feel and direction of Swift?

Yes. Elegant. Easy to read. Possibly cryptic for a new user, but worth it. Fits with the precedent of closures.

  • How much effort did you put into your review?

Read the pitch and related posts. Reviewed the proposal.

+1. Thank you to the author for putting this forward.

-1. Maybe -0.5 for single-expression computed properties.

I don't think so. Code including the return keyword seems clearer and more explicit.

It aligns with the closures syntax but it also removes some clarity from the code.

N/A.

Reading the pitch and the proposal.

4 Likes

Allow return to be omitted from single-expression…

Functions: No thank you, return is important here. We cannot omit any part of a func declaration unless it returns Void, and we should keep it that way.
-1

Properties: Yes please, return is inconvenient here. Simple computed properties are often written on a single line, and the extra keyword just adds noise.
+1

Subscripts: Yes for get blocks, neutral for implicit getters but probably worth being consistent with properties. (I also wish subscripts used a colon instead of an arrow to specify their type, since they are more property-like than method-like what with being assignable and all.)
+½

Initializers: No thank you, return is of limited utility here, and therefore important to write explicitly when it is used.
-1

18 Likes

I like the proposal for read-only computed properties and read-only subscripts where we already omit get { and the matching }. I think it would fit well with the existing implicit return support in single-expression closures.

I don't like implicit return for the other cases (read-write properties, read-write subscripts, funcs, and inits).

My experience with implicit return, outside of Swift, is primarily in Scala, which uses implicit return for all expressions in tail position. The Scala community generally frowns on explicit return, and this community preference is one of the things I dislike about Scala.

I followed the entire discussion of this proposal.

6 Likes

Agree with many of the opinions here: seems like a good fit for one-line getters, not sure it’s a net win for functions in general and would therefore not add such a feature for all such cases.

I have used languages with implicit return extensively as well as languages that don’t. I’ve thought about this both during the current pitch and on past discussions.

4 Likes

-1

Not at all. I think the drawbacks outweighs the benefits [see the answer below].

No.
It will confuse people, specially newcomers; they will ask themselves why that function does not have a return keyword, then google it and realize that for some reason Swift doesn't need the return keyword on single instruction functions.

N/A

A quick reading of the proposal and read the comments above.

2 Likes

I'm pretty strongly negative about this proposal.

This is optimizing out a single keyword at the expense of clarity, and adding special cases to the compiler and type checker. The optimized out keyword does not improve clarity or maintainability of code. Any value it claims to add is heavily offset by the costs.

This is a sugar proposal that does not improve expressivity at scale (unlike some of the other recent sugar proposals) because it is at the statement/function granularity, it does not improve the subexpression granularity which is where multiple compositions of sugar can help improve readability and maintainability.

The comparison to closures also misses an important point: the entire reason we sugared this for closures in the first place is that there are cases where the return keyword obscures the intent of the closure because they are otherwise very small - think things like x.map {$0+1}.filter { isPrime($0) } - the additions of returns really would harm clarity. The corresponding concern does not exist in function (including nested function) or accessor context, because there is already a ton of ceremony associated with the declaration, the type signature, etc.

No.

No. There is a variant of this proposal that would fit much better in my opinion, which is to use distinct syntax for this:

func sum() -> Element = reduce(0, +)

Such syntax achieves several things beyond the proposal, including eliminating ambiguity (with human readers), making it clear it shouldn't magically be forced to work in the void case with a non-void callee, and achieving a higher goal of allowing "one liners" that the proposal fails to achieve. It also eliminates the braces, which some could see as positive, and some as a negative.

It still isn't clear to me that this is worth doing though, for the reasons mentioned above.

I read the proposal, I also participated in many prior discussions over the course of years about this.

-Chris

27 Likes
  • What is your evaluation of the proposal?

Big +1. I think this makes the language feel more consistent and expressive. The return keyword does not communicate anything of value in these contexts. I think most Swift programmers will enjoy the sugar once they are aware it is available and start using it.

I very much agree with the decision to limit this sugar to single-expression bodies. I think the contexts in which single-expression returns can be confusing are limited to contexts in which statements are also present. This proposal nicely avoids those issues.

The initializer case is of little value on its own, but I don't think including it is problematic and it increases overall consistency.

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes. It's a relatively minor, but very nice and convenient form of syntactic sugar. Removing meaningless syntactic details from our code increases overall clarity.

  • Does this proposal fit well with the feel and direction of Swift?

Very much so. Swift embraces a very thoughtful balance between clarity and convenience. In cases where syntax adds no meaningful clarity for experienced programmers we are usually able to elide it. This proposal is very much inline with this direction.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Yes. I have used several lanaguages with similar features. I think this proposal captures the benefits of the sugar without the potential for confusion that can be introduced when it is mixed with statements.

I want to call out the alternative sugar for function syntax that was inspired by other languages:

func square(of x: Int) -> Int = x * x

I agree with everything the proposal authors have to say about this alternative in particular and want to point out that the difference in number of characters in the syntax is exactly one (the = is replaced by an opening brace and a closing brace is added on the end):

func square(of x: Int) -> Int = x * x
// vs
func square(of x: Int) -> Int { x * x }

I admit that the former syntax feels lighter despite only one character difference. However, I think the latter is more appropriate for Swift. In particular, I think this syntax is a bit confusing when used with getters, especially get-only computed properties where it cannot be used without braces because it would conflict with stored properties:

// stored property
var x: Int = 42
// computed property using brace syntax
var x: Int { 42 } 
// a computed property using `=` syntax needs to use braces and say `get`
// in order to distinguish itself from a stored property
// IMO this issue is a dealbreaker for `=` syntax in the context of getters
var x: Int { get = 42 } 

Swift has very intentionally placed all code bodies inside of braces. I think this is a good decision and we should stick with that direction.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I have followed all the discussion threads and read the final proposal (as well as current reviews).

2 Likes

I'm coming around to Rob and Nevin's (slightly different but both good) views on this. I want this all the time for property and subscript getters, but don't really care for functions, and think keeping the return makes a function minorly easier to read. I think a big part of that is because functions can return Void and so I have to think about whether the function has a (non-Void) return type or not, whereas properties and subscripts with Void type are almost nonexistent.* So I'd say we should take this for properties and subscripts and not for functions and inits, and I even think it makes sense to limit it to the get-only shorthand syntax so that it's not juxtaposed with a setter that is Void-returning.

* They can come up for conforming to protocols sometimes, sure.

11 Likes

I agree, except for limiting to use for get-only properties. I don't think that

var prop: Type {
    get { va() }
    set { ... }
}

is particularly confusing. The only thing a single expression in a getter can be doing is returning the value for the getter. If anything, it's more clear when the get keyword is present. I don't buy that users would wonder about the setter, because there's no expectation of a value being returned to begin with.

1 Like

-1

I think that there is clarity lost here. It feels natural to omit inside of an anonymous closure like .map { $0 * 2 }, but I'm not convinced that losing a word else is an improvement for language clarity.

3 Likes

What is your evaluation of the proposal?

+1 for the general idea for not so much for the implementation.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes. But I think the issue why this is being brought up is that our closures have different syntax and lots of magic. We should bite the bullet adopt fat arrow clousure syntax.

func square(of x: Int) -> Int { x * x }
// vs
let square = (of x: Int) => Int { x * x }

Does this proposal fit well with the feel and direction of Swift?

Somewhat.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Swift closures are different than what I have used in c# and ES6

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Participated in the pitch.

:+1:t2:

I'm a strong supporter of this proposal. I'm totally accustomed to implicit single-expression returns in closures, and I think that if this proposal is accepted, it will quickly feel natural to have that feature everywhere. I think the single-expression rule does a great job of maintaining clarity, and of preventing Swift from feeling like we're sliding toward a language with implicit last-expression return (which I really dislike).

3 Likes

I’m a big -1 for including this for functions. +0.5 for read-only computed properties. I participated some in the pitch thread and read through the proposal. I still don’t think it has been demonstrated that this “inconsistent” syntax has caused any real issues other than a slight annoyance, and as Chris notes, the clarity sacrifices are great. I think advocates of consistency should justify why this one difference in syntax between functions and closures needs to be unified, but others shouldn’t. Function declarations are quite verbose, without much other opportunity for sugaring away syntax. Even the #argument syntax from the early days was removed in favor of specifying an empty argument label explicitly.

I’m more supportive of enabling this for get-only subscripts and computed properties, since I think there are actually clarity improvements in expressing that a computed property is essentially a proxy for a more complex access on some other member. It lets the access read almost as a macro (in the sense that you can conceptually substitute some.long.accessor.chain for property wherever it appears). As soon as we start using the more verbose get/set syntax, though, there are quickly diminishing returns for eliding the extra return and the simple “proxy” understanding starts to become more clouded.

Closures and functions declarations are fundamentally different constructs in the language, and they are separate for a reason. Pursuing parity between the two should be justified based on something other than the abstract notion of “consistency” which is violated in many other ways between the two.

  • What is your evaluation of the proposal?

Strong +1

When Swift was first released, my team was in the middle of writing an iOS app in Objective-C. We’ve decided to give Swift 1.0 a try and eventually ended up writing most of the app in Swift (big mistake, I still have migration nightmares).

The things that initially attracted me the most about the language were small. I mean things like type inference, shorthand argument names, implicit return from closures (of all things)… the list goes on.

So, in the context of things I love about Swift, this addition feels natural.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I have participated in the original discussion and read the proposal.

I think this applies to an explicit get { some.other.property } construct as well. It reads as "get the value stored in some other property", instead of "get the value returned by the closure which returns the value of some other property".

2 Likes

That’s a decent point. get { something.else } reads better than get { return something.else }. In any case, I definitely don’t think that implicit and explicit get should behave differently, so if we allow the implicit return for one, it should work for the other as well.

1 Like

+1

Yes. It broadens the explanation of when you can use a single expression without a return in a simple to process way.

Yes.

I like that it isn't quite as far as ruby's handling expressions.

I've followed to conversation and read the proposal.