SE-0345: `if let` shorthand for shadowing an existing optional variable

Hello Swift community,

The review of SE-0345 "if let shorthand for shadowing an existing optional variable" begins now and runs through March 22, 2022.

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 the review manager. When emailing the review manager directly, please keep the proposal link at the top of the message.

What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • 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?

More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

Doug Gregor
Review Manager

50 Likes

The proposal is relatively well thought out. It has one slight issue that is somewhat limited in utility: while let val { ... } syntax is dubious of utility in my view. That objection is quite minor in comparison to the reduction of restating ones self with current let syntax.

It is a good quality of life improvement and does merit a change; but if I were to compare importance of this to other things it does not rank as high (this is not meant as a slight against the idea or the author, I just think there are bigger fish to fry that if we are short on resources this might not be the most impactful change to address).

Directionally speaking, this lines up nicely with the progressive disclosure concepts of swift and lines up nicely with not needing to retype unneeded words.

I have not used similar items to this shorthand (maybe shy of macros in C). But I have written enough unwraps like that to see the utility.

Per the review depth: relatively shallow read of the proposal but I was following the pitch thread closely.

I still think if let foo? reads better, but not to the point where it's a deal breaker. As long as the let stays I'm happy.

+1

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

Yes, barely.

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

Yes.

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

I've only ever played with Kotlin a little bit, but this definitely seems better than their flow based nulliblity analysis.

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

I only did a quick reading of the proposal, but I had been actively involved in the pitch thread, and was already aware of the arguments in the proposal.

4 Likes
  • What is your evaluation of the proposal?
    +1

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

Not a "problem" for me personally, but folks seem to feel it will be helpful. if var foo { ... } as a potential tripping hazard for new users means good documentation for this is important.

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

sure.

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

N/A

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

Followed most of the (long) pitch thread and read the proposal over.

Thumbs up. I suggested this in FB5824511 before Swift was even open source :wink:

10 Likes

-1

My view on this has not changed since the pitch thread. It makes code much harder to reason about as the foo in if let foo { } stands for both the optional outside the closure and non-optional inside. It doesn't address any significant problem, just saves a few chars typing. But those are useful chars which make it much clearer what is going on.

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

No, as it goes against the current practice of each symbol in each context having one and only one meaning. This though stands for two things.

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

I know of nothing like this in other languages.

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

I've been following this since the original pitch. and studied the document then, glanced over it again now.

9 Likes

-1

I am very much against this proposal.

let x: Int? ...

func f() {
  if let x { ... }

The x of if let x will now literally refer to two variables simultaneously. This seems nonsensical and confusing.

15 Likes

-1

Let’s wait for the borrow checker’s dust to settles before we sugar unwrapping. In my mental model unwrapping should be a form of performant binding.

if ref, if inout, if move, etc is going to get more complex. Instead of sugaring for shadowing variables we should sugar away var, let, ref, inout, move, etc by picking the best semantics for if unwrap foo {..}

8 Likes

While I don't have any strong opinions about whether this proposal should be accepted before we have better control over ownership, I think it's fine to accept it now, but with a clearer indication of optional unwrapping. The fact that this syntax makes sense to us is partly due to our knowledge of Swift. If I were to see this in another programming language, perhaps with different keywords, such as if (val foo) it wouldn't be immediately obvious what that syntax does. There's no clear indication of optional unwrapping, with the let keyword serving as a subtle symbol only to experienced Swift users.

The ? and ! for optionals in general and force unwrapping respectively are prominent throughout Swift. From optional chaining to the ?? operator and the case let syntax, the question mark pops up in a lot of places. An if let x? syntax would likely be more searchable as well, easing the learning curve for beginners, which is important since Swift is still relatively young. While I understand concerns with consistency —essentially allowing seamlessly turning if let foo = foo into if let foo— Swift has systematically chosen to opt for readability. For example, the syntax for declaring computed properties and functions, while fundamentally similar, have separate keywords (var, func) and syntactical constructs (parentheses, -> for the return type, etc.). Readability would be better preserved with an indication of optionality, since the question mark will draw the attention of the reader to that construct. Thus, having an indication of optionals in this new syntax will not only be vital for preserving readability for all, but also ease the learning curve for beginners.

Lastly, the proposal cites the awkwardness of type annotations as a disadvantage of this syntax, but I believe this to be a superficial concern, that we'll quickly get accustomed to, especially with proper syntax highlighting.

Overall, I'm excited to see this proposal finally reach proposal status! :slight_smile:

1 Like

+1 as written. I don't think the current syntax of restating the same variable names provides much value. It's just syntactic noise. I don't mind the other suggestions about if let value? spelling, I just think an additional character is unnecessary here. This sugar will be a very common occurrence in codebases and naturally be a part of any Swift programmer's working lexicon — it can be short and sweet. And for newcomers, it is no less confusing than the status quo of repeated variable name declarations.

8 Likes

I didn't see this use case in the proposal, so I'm asking here:

doSomethingWithCompletion { [weak self] in
    guard let self else { return }
    ...
}

Would this be allowed as well?

13 Likes

+1

As a professional app developer I’m completely for this proposal as it’s described. Especially as I’m dyslexic having to type the variable name twice is annoying. (Yes we finally got autocomplete on Xcode for it, but not all Swift is written in Xcode, and if anything it shows that it’s a shortcoming on the language)

I don’t understand the arguments being made about the name referring to variables simultaneously. We have that currently, and assigning a variable to itself is weird anyway.

9 Likes

-1

I've followed most threads since the original pitch but this is my first time weighing in.

I think this is a level of syntactic sugar too far. The fact that if let x = x essentially sugars if case let .some(x) = x (for Optional only) is unfortunate in my opinion (opposed to if let performing pattern matching by default) but obviously this cannot be changed at this point.

The syntax, as proposed, reads to me as a completely new uninitalized variable declaration and is not immediately clear that it is shadowing an existing variable with the same name. I would be much more in favor of the proposed if let x? syntax, which gives the additional context to the programmer that this is pattern matching an Optional.

22 Likes

-1

In context of language and language design its quite important not to try skip some rudimentary but still deterministic way of conditionally unwrapping a variable but to make things as clear as possible when reading code in other merge or pull requests. In this proposal we try not to reason about how we actually should work with optionals but try to skip some necessary part of working with them by introducing some not obvious quite new way of declaration of variable in Swift.
Say for example this:

var x: Int? = 5
if var x {
// do something with x
}

We nowhere has it in our language. Compiler always require us to write down an explicit type or assign a value to it in order for it to determine the type implicitly.

There
if var x
nothing is what we usually see or deal with in our codebases.

BTW, great example by @avi

4 Likes

Yes!

doSomethingWithCompletion { [weak self] in
    guard let self else { return }
    ...
}

would be equivalent to writing

doSomethingWithCompletion { [weak self] in
    guard let self = self else { return }
    ...
}
9 Likes

+1 as written.

  • no new keywords required
  • noise reduction
  • absolutely no loss of information conveyed.

What's not to love?

13 Likes

I'm very glad to see this come to review and believe it is an important quality of life improvement. Some form of sugaring for scoped "unwrap this and let me work with it" is a very frequently requested feature (see @chockenberry 's Let's Fix if let Syntax thread from last year, for example), and the source of some not inconsiderable Kotlin envy from developers working with both languages.

But this proposal solves the ergonomics in a way that I believe fits neatly into the existing language, in a way imitating Kotlin's != nil type refinement approach would not. It is simple to explain – you are just eliding the = x in if let x = x – and fits with the many other places in the language where you can elide "obvious" implied ceremony, leaving clear code that feels more lightweight. This simple model of eliding the rhs of the assignment while keeping the let or var also means it will scale neatly to new introducers for shared and mutable borrows.

Here and in the pitch thread, the majority of opposition is regarding shadowing and potential ensuing confusion from shadowing. Many comments here ignore or deny the fact that this shadowing happens today with exactly the common idiom this proposal aims to sugar. Like it or not, shadowing is a reality for Swift, so merely saying "this is bad because it shadows a variable" is insufficient reason to oppose this sugar.

A case against would need to go further such as:

  • if let x is less clear in its shadowing intent than if let x = x; or
  • Sugaring if let x = x encourage this idiom, and this would be bad because it encourages shadowing and shadowing is bad, developers should instead try to write if let y = x to distinguish the wrapped and unwrapped values.

Neither of these seem credible to me. I just do not believe anyone would find the absence of the = x on the rhs suddenly means that the shadowing of the unwrapped value is any less clear. One reason why this proposal is better than Kotlin's approach is that the let remains. In fact the dropping of ceremony (especially with long variable names) might make it more clear. As to encouraging people to find a different name for the unwrapped value... everyone knows naming is the hardest problem in computer science, so that just seems cruel (see the "let's fix if let" thread for a longer less flippant reason why forcing finding of new names harms readability :).

35 Likes

+1

I like this proposal but I personally don't like that this also applies to implicit self. Until now, when using implicit self, someProperty could always be substituted with self.someProperty.

Before:

if let someProperty = self.someProperty {
  ...
}
// or
if let someProperty = someProperty {
  ...
}

After:

// this would be possible:
if let someProperty {
  ...
}
// but this would be wrong:
if let self.someProperty {
  ...
}

I think this is too much implicit syntax and can be quite confusing. There will anyways be a lot of use cases where the proposed syntax improvements can't be used (for example if let someProperty = someObject.someProperty).

11 Likes

Similar to overloading, this is generally an argument against a feature Swift has today, which just happens to interact with this proposal. Implicit self. is not to everyone's taste and certainly is a trade-off, but this was settled as SE-0009: Require self for accessing instance members.

The beauty of this proposal is that it is very straightforward: you can omit the = x on the right-hand side when you would otherwise write if let x = x. This makes it simple to explain, and simple to remember. All behavior, including supporting var and implicit self, falls out of that. And this is why if let someObject.someProperty or if let self.someProperty will not compile.

Exceptions to this simple rule could be surprising and annoying, and would need to be justified by demonstrable increased harm of this sugar compared to the unsugared case. While hypothetical harm can be described, that is not enough; the bar should be credible material harm introduced by this proposal.

20 Likes

Something that i did not see discussed in the unwrap spelling is that instead of introducing a new shadow variable the unwrap could “bind” to the optional variable declaration so that if it’s var then the unwrapped version is var. same for let or future ownership versions.

This should address the sentiment of “just unwrap and let me work” and also future proof us for ownership.

var foo = someOptinal()
if unwrap foo {…} // foo here is var

let bas = someOptinal()
if unwrap bas {…} // bas here is let

ref third = someOptinal()
if unwrap third {…} // third here is ref

5 Likes