`if let` shorthand

The system is already designed. People are already using it. The only question here is a minor convenience to the spelling.

It’s like laying out a rectilinear grid of sidewalks, and observing that after several years one particular corner is so heavily taken that a small diagonal path has been worn across the grass.

And then when someone suggests “Maybe we should make a little diagonal section of walkway there”, you say what?

“It’s extremely dangerous to give people a path where they want to walk”?

“Some people might think the path goes in a totally different direction”?

I think you’re making this into something way bigger than it actually is.

• • •

if let x {” is so clear and so expressive, that people keep asking, year after year after year, why on earth it doesn’t work already. Why it wasn’t part of the original language when Swift was first released. It’s such a natural spelling for such a common concept, which fits so cleanly into the rest of the language, that not having it makes things feel somehow “off”.

If people completely new to Swift find themselves consistently expecting this spelling to work, and they all understand exactly what it should do, then what on earth are we trying to accomplish by persisting in directly contravening their common intuition?

There is zero danger of confusion here. The whole reason this feature keeps being proposed, is that it is such a glaringly obvious thing to have.

And by consistency, that extends to both the guard and var versions as well.

Introducing a whole new keyword for this seems like using a sledgehammer to tap in a nail to me. It would also be strange to introduce the term unwrap for this one specific form of unwrapping.

"A new concept" is a fairly nebulous term but I can't see how this can be described as such. It's simple sugar that if let x = x can be written if let x. There are plenty of other places in the language where you can omit "obvious" things to reduce noise.

It's a good thing to support (and in keeping with what if let x would desugar to) for the same reason if var x = x is a good thing to support. Sometimes it's useful to have a mutable copy, and having to declare and assign it on another line is unhelpfully verbose.

This seems like an exaggeration. The var in if var x is pretty obvious. I don't think the claim it would "betray a lot of expectations and introduce a lot of bugs." is credible. Does if var x = x do so? I don't believe so, and I don't think dropping the = x materially changes that.

21 Likes

My problem with it is implicit shadowing. I can’t think of any other example of that in the language (consider the way initializers often work, with explicit self assignment), and it comes with obvious readability pitfalls.

And I still don’t understand why this is helpful. Should this be done for for-loops too?

1 Like

My preference is for "if let x", but "if unwrap x" isn't bad either. Here's some code to get a feel of it:

if unwrap x {
   print(x)
}
if unwrap x, unwrap y, unwrap z {
   print(x + y + z)
} else if unwrap u {
   print(u)
}

I think it reads well. It might be less clear that you're creating a new variable with this (compared to if let), but this might not matter much if that variable is immutable and is shadowing the previous one.

The only (potentially surprising) situation exposing it's a new variable would be:

if unwrap x { // x is self.x here
   self.x = 1 // x is different from self.x here
}

Unfortunately, I don't think we can forbid this while keeping the model reasonable. This is why I'm hesitant to let go of the let (that signals a new variable) and replace it with unwrap.

1 Like

When ownership comes in, we will have borrowed values as arguments. It will be extremely natural to want to project a borrowed optional into a borrowed underlying element, exactly like "if let" does, but without the copy.

Is there a proposal for how this will be handled?

2 Likes

The performance roadmap has a start. Maybe if ref, or whatever it ends up being called?

Changing the subject back, I firmly believe that explicitly naming optional bindings is important for code clarity. And if there’s disagreement on that front, it would be better to use shorthand arguments ($0, etc.) instead of implicit shadowing. It’s not like the latter would provide any further information.

1 Like

It depends what you mean by uncommon, but I searched a few large projects and it appeared several times in each one, and all the cases I checked looked legit. It's obviously a tiny number compared to if let but I'm not sure if that's relevant. The if var variant would be consistent, harmless (or at least not differently harmful to the existing desugared syntax), and useful.

5 Likes

Right, I'm not arguing whether they are correct or not (surely var pattern bindings are useful!), I'm asking whether they are important to sugar. It sounds like the answer is "no", that sugaring them doesn't provide an important ergonomic benefit.

I come back to the harm of sugaring them. People seem to be content stating that "it is obvious what if var x { is defined by analogy to the existing syntax. But no one seem to be addressing the problem of "what does a reference to x mean in a scope mean in the face of this"? Changing x from immutable to mutable - or to be a mutable copy of mutable data that isn't tied its original value - isn't something you want to make lightweight and nearly invisible in source.

You also don't want to make it impossible - it is a useful thing (see above) but making it less visible isn't beneficial in my mind.

The reason I asked about the borrow version of this is that we're about to embark on a massive expansion of the type system for ownership. That expansion will need to answer all the same sorts of questions that we've had in the "copy things" part of Swift. Given that we've lived for years without sugaring this, it seems worthwhile to me to get the ownership support further along, to make sure we can come up with a coherent solution to works well with it as well as with Swift 1 features.

-Chris

9 Likes

if var x = is very uncommon; according to a quick GitHub search there are about 50 to 100 "if let x" for every "if var" usage (and in many cases "if var x = " usage is unjustified and is just a sloppy coding). I'd speculate "if var x = " is less needed than an ability to have a "var x: T" function parameter - something we had in original swift but then removed.

...

Nevin: I am sure you mean well, but I would find your arguments more convincing if you addressed the points that I and other people are raising, instead of making undefended unilateral statements/claims that they are just wrong.

-Chris

5 Likes

Given how idiomatic Swift operates, I’d go so far as to say that var in general isn’t that common.

After all, value types are (more or less) inherently immutable, such that “mutation” is really just “replacement”. Add lazy method composition, and a lot of Swift might as well be Haskell.

It’s not the syntax that’s the problem it’s the semantic meaning. if var x=x is a special case of if var closureName = someVariableName. That’s syntax that you can use coherently in something like:

if var counter=someVariable{
      [do stuff with the counter here]
       return counter}

*obviously with the counter having a name that is descriptive of how you’re using it in the closure

But I struggle to imagine a situation where creating a mutable shadow makes semantic sense. I’d love to see motivating examples here.

2 Likes

Let's please avoid those kinds of generalisations and disparagements. It's a feature in the language - if people want to use it, they are very well entitled to do so.

It is actually a compiler warning if a var is not mutated, so presumably those cases are still examples of correct use.

7 Likes

My only concern with if let x { and if var x { is that they worsen a situation which is already not ideal.

if let a = foo() { ... } has to be learnt. It is unique to Swift, and I never met anyone who can guess what it does. I have been teaching this Swift construct several times. The answer is usually :roll_eyes: - and then we move on.

if let x = x { ... } adds another difficulty: one has to understand that a variable is shadowed inside the brackets. Again, this is impossible to guess.

In this landscape, if let x { ... } is just another layer of arbitrary syntax that is only obvious for those who can afford such knowledge.

(Aside: the relationship between if let x = x and if case .some(let x) = x, I mean the fact that if let x = x is a form of pattern-matching, has never sounded convincing to me, regardless of the genesis of this syntax: I can't learn if let x = x from other pattern matching syntaxes).

To me, all claims of "obviousness" I read here sound not convincing at all. This is, on the contrary, one of the less obvious part of the language. I don't say it is difficult to learn and use. Swift has excellent support for the Optional concept.

All in all, I don't see at all the necessity of the if let x { sugar. If it ships, I'll use it of course, and even enjoy it, because I am "in the know".

23 Likes

Karl, my comment was based on what I saw on GitHub, you can see it for yourself:

https:// github. com /search?q=%22if+var%22+filename%3A*.swift&type=code (remove spaces)

1 Like

I agree that Optional binding is one of those things you really do need to learn in the first place, but I don’t think there’s much room for improvement there.

In fact, I’d argue that one of the most important things you need to know (that is, things that aren’t fairly easy to pick up on the fly) in Swift is what Optionals are (seeing the actual implementation is quite illuminating), the methods of handling them, and which method to use when.

Things like that are distinguished by being easily overlooked in favor of antipatterns, such that someone may write a considerable amount of Swift code without realizing there are better ways.

1 Like

This frames "uncommon" in terms of the ratio of times var is needed versus let but that's really measuring how incredibly common if let is.

By a different measure, you could describe if var as very common. I've checked over a dozen projects and found it in every one. That includes MovieSwiftUI, NetNewsWire, Apollo, Carthage, Attabench, and the swift standard library, driver, and package manager.

4 Likes

I fully agree with you. The way optionals are handled in the language is great.

1 Like

It bugs me that one's opinion could rely heavily on their naming discipline and codebase convention. The number of characters you save is roughly three + the variable length. So depending on your average variable length, it could either be a blessing or a minor quirk you now need to learn. It's easily a polarizing feature.

Also, names very close to the original variable (foo), such as ones with prefixes (selfFoo) or suffixes (foo1), get absolutely nothing out of it. They are also another group of common cases when you need to differentiate similar variables, add extra information, avoid shadowing, etc. That on its own is not a problem, but one could try to bend their convention to increase the chance of utilizing this. It, in disguise, is blessing a particular group of code conventions.

5 Likes

I don't think if let x = x { is an antipattern. We are not discussing a way to fix a harmful language idiom, here.

2 Likes