Proposal Sketch: simplify optional unwrapping syntax


(Daniel Hooper) #1

A very common pattern in swift code is to "guard let" or "if let" optionals
- this works by creating a new non-optional variable to be used by future
code. Often, the new variable is given the same name as the original
optional variable, creating a shadow variable. This approach leads to odd
looking code like this:

if let nearestX = nearestX { closest = nearestX }
guard let layer = layer else { continue }
// use layer

At a glance, and to Swift newcomers, this code looks completely
non-sensical. It's also verbose for simply ensuring the variable is
non-nil.

The solution:
Swift should generate unwrapped shadow variables after nil checking. The
compiler would treat the below code as if it had created an unwrapped
shadow variable.

if nearestX != nil { closest = nearestX } // notice that nearestX isn't
force unwrapped
guard layer != nil else { continue }
// use layer, without force unwrapping

Why force unwrapping isn't a good alternative:
You might suggest force unwrapping variables when they're inside an if or
after a guard that checks for nil. While this does allow for the "layer =
nil" syntax, it results in code that is less resilient to change. Imagine
that this code was written:

{code:java}
if layer != nil {
// lots of code before //
layer!.backgroundColor = newColor
// lots of code after //
}
{code}

And much later, you need to use some of the the code in the if body
elsewhere, so you copy and paste a huge chunk of it. You likely won't
notice the force unwrap, and unless you're lucky, you probably didn't paste
it into an if that checked layer for nil. So you get a crash. Because of
this, it's important we make safe optional unwrapping as easy and sensical
as possible, and minimize the situations that you would need to force
unwrap.


(Jeff Kelley) #2

I’ve had similar ideas to this. Instead of ditching the if let syntax altogether, another approach would be to use the existing name if no new name is given, so that this code:

  if let foo = foo { /* use foo */ }

could become this code:

  if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it another name with the if let syntax, that would work as it does today.

Jeff Kelley

SlaunchaMan@gmail.com | @SlaunchaMan <https://twitter.com/SlaunchaMan> | jeffkelley.org <http://jeffkelley.org/>

···

On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution <swift-evolution@swift.org> wrote:

A very common pattern in swift code is to "guard let" or "if let" optionals - this works by creating a new non-optional variable to be used by future code. Often, the new variable is given the same name as the original optional variable, creating a shadow variable. This approach leads to odd looking code like this:

if let nearestX = nearestX { closest = nearestX }
guard let layer = layer else { continue }
// use layer

At a glance, and to Swift newcomers, this code looks completely non-sensical. It's also verbose for simply ensuring the variable is non-nil.

The solution:
Swift should generate unwrapped shadow variables after nil checking. The compiler would treat the below code as if it had created an unwrapped shadow variable.

if nearestX != nil { closest = nearestX } // notice that nearestX isn't force unwrapped
guard layer != nil else { continue }
// use layer, without force unwrapping

Why force unwrapping isn't a good alternative:
You might suggest force unwrapping variables when they're inside an if or after a guard that checks for nil. While this does allow for the "layer = nil" syntax, it results in code that is less resilient to change. Imagine that this code was written:

{code:java}
if layer != nil {
// lots of code before //
layer!.backgroundColor = newColor
// lots of code after //
}
{code}

And much later, you need to use some of the the code in the if body elsewhere, so you copy and paste a huge chunk of it. You likely won't notice the force unwrap, and unless you're lucky, you probably didn't paste it into an if that checked layer for nil. So you get a crash. Because of this, it's important we make safe optional unwrapping as easy and sensical as possible, and minimize the situations that you would need to force unwrap.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Marc Knaup) #3

One problem with dropping if/guard let is something like this:

if let x = a?.b.c?.d, y = something(x) {
   …
}

would then have to become this:

let x = a?.b.c?.d
if x != nil {
   let y = something(x)
   if y != nil {
       …
   }
}

I'm fine with
   if let x
as a shortcut for
   if let x = x

It just reads a bit weird - like declaring an immutable variable with no
type and no value.

···

On Fri, Dec 11, 2015 at 5:19 PM, Jeff Kelley via swift-evolution < swift-evolution@swift.org> wrote:

I’ve had similar ideas to this. Instead of ditching the if let syntax
altogether, another approach would be to use the existing name if no new
name is given, so that this code:

if let foo = foo { /* use foo */ }

could become this code:

if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it
another name with the if let syntax, that would work as it does today.

Jeff Kelley

SlaunchaMan@gmail.com | @SlaunchaMan <https://twitter.com/SlaunchaMan> |
jeffkelley.org

On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution < > swift-evolution@swift.org> wrote:

A very common pattern in swift code is to "guard let" or "if
let" optionals - this works by creating a new non-optional variable to be
used by future code. Often, the new variable is given the same name as the
original optional variable, creating a shadow variable. This approach leads
to odd looking code like this:

if let nearestX = nearestX { closest = nearestX }
guard let layer = layer else { continue }
// use layer

At a glance, and to Swift newcomers, this code looks completely
non-sensical. It's also verbose for simply ensuring the variable is
non-nil.

The solution:
Swift should generate unwrapped shadow variables after nil checking. The
compiler would treat the below code as if it had created an unwrapped
shadow variable.

if nearestX != nil { closest = nearestX } // notice that nearestX isn't
force unwrapped
guard layer != nil else { continue }
// use layer, without force unwrapping

Why force unwrapping isn't a good alternative:
You might suggest force unwrapping variables when they're inside an if or
after a guard that checks for nil. While this does allow for the "layer =
nil" syntax, it results in code that is less resilient to change. Imagine
that this code was written:

{code:java}
if layer != nil {
// lots of code before //
layer!.backgroundColor = newColor
// lots of code after //
}
{code}

And much later, you need to use some of the the code in the if body
elsewhere, so you copy and paste a huge chunk of it. You likely won't
notice the force unwrap, and unless you're lucky, you probably didn't paste
it into an if that checked layer for nil. So you get a crash. Because of
this, it's important we make safe optional unwrapping as easy and sensical
as possible, and minimize the situations that you would need to force
unwrap.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #4

This was discussed in the old forums... ah, those times :slight_smile:

As before, I don't think this idea is workable, because this only works for
names that describe local variables and only if they are not captured by
long-running closures.

Imagine the code

class C {
  var layer: CALayer? { can be changed from other threads }

  func f() {
    var nearestX = X()

    doSomethingAsync {
        nearestX = compute(...)
    }

    // later

    if nearestX != nil {
      // *can't* use nearestX without unwrapping
    }

    guard layer != nil else { continue }
    // *can't* use layer without force unwrapping
  }
}

This will, however, work if we do create a new name that shadows an old
name with if let nearestX = nearestX or guard let layer = layer.
let here plays an important role by reminding that original nearestX or
layer may have already changed in the meantime.

I agree this shadowing may not look best if nearestX and layer *are* plain
old local variables. In that case those names are under your control and I
would find this refactoring benefitial:

    let maybe_nearestX = ...
    let maybe_layer = ...

    if let nearestX = maybe_nearestX {
      // use nearestX
    }

    guard let layer = maybe_layer else { continue }
    // use layer

···

On Fri, Dec 11, 2015 at 7:11 PM, Daniel Hooper via swift-evolution < swift-evolution@swift.org> wrote:

A very common pattern in swift code is to "guard let" or "if
let" optionals - this works by creating a new non-optional variable to be
used by future code. Often, the new variable is given the same name as the
original optional variable, creating a shadow variable. This approach leads
to odd looking code like this:

if let nearestX = nearestX { closest = nearestX }
guard let layer = layer else { continue }
// use layer

At a glance, and to Swift newcomers, this code looks completely
non-sensical. It's also verbose for simply ensuring the variable is
non-nil.

The solution:
Swift should generate unwrapped shadow variables after nil checking. The
compiler would treat the below code as if it had created an unwrapped
shadow variable.

if nearestX != nil { closest = nearestX } // notice that nearestX isn't
force unwrapped
guard layer != nil else { continue }
// use layer, without force unwrapping

Why force unwrapping isn't a good alternative:
You might suggest force unwrapping variables when they're inside an if or
after a guard that checks for nil. While this does allow for the "layer =
nil" syntax, it results in code that is less resilient to change. Imagine
that this code was written:

{code:java}
if layer != nil {
// lots of code before //
layer!.backgroundColor = newColor
// lots of code after //
}
{code}

And much later, you need to use some of the the code in the if body
elsewhere, so you copy and paste a huge chunk of it. You likely won't
notice the force unwrap, and unless you're lucky, you probably didn't paste
it into an if that checked layer for nil. So you get a crash. Because of
this, it's important we make safe optional unwrapping as easy and sensical
as possible, and minimize the situations that you would need to force
unwrap.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Lattner) #5

Hi Jeff,

This is commonly requested - the problem is that while it does help reduce boilerplate, it runs counter to the goal of improving clarity.

-Chris

···

On Dec 11, 2015, at 8:19 AM, Jeff Kelley via swift-evolution <swift-evolution@swift.org> wrote:

I’ve had similar ideas to this. Instead of ditching the if let syntax altogether, another approach would be to use the existing name if no new name is given, so that this code:

  if let foo = foo { /* use foo */ }

could become this code:

  if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it another name with the if let syntax, that would work as it does today.


(Chris Lattner) #6

Hi Daniel,

We discussed this extensively (and for a while this was plan of record to implement) but it has some pretty significant challenges. The main problem is that it makes it extremely unclear and unpredictable what the type of a variable is. One of the primary reasons we constrain type inference to a statement boundary to is to make it more clear what is going on. You don’t have “spooky type action at a distance” like you do in some other languages with whole-function type inference.

Type confusion is particularly problematic here because the different cases you have depending on what T? is:

- With something like Int?, there are many operations that make sense on Int but not on Int?
- With something like [Int]?, there are operations (like map) that make sense on both types.
- Some operations are completely common between the two (e.g. “let x = v”).

Further, swift has an implicit promotion from T to T?, which can cause extremely confusing downstream errors if you’re not aware of what is going on. There are also implementation concerns as well.

-Chris

···

On Dec 11, 2015, at 8:11 AM, Daniel Hooper via swift-evolution <swift-evolution@swift.org> wrote:

A very common pattern in swift code is to "guard let" or "if let" optionals - this works by creating a new non-optional variable to be used by future code. Often, the new variable is given the same name as the original optional variable, creating a shadow variable. This approach leads to odd looking code like this:

if let nearestX = nearestX { closest = nearestX }
guard let layer = layer else { continue }
// use layer

At a glance, and to Swift newcomers, this code looks completely non-sensical. It's also verbose for simply ensuring the variable is non-nil.

The solution:
Swift should generate unwrapped shadow variables after nil checking. The compiler would treat the below code as if it had created an unwrapped shadow variable.


(Sean Heber) #7

This has come up a few times. I’ve thrown my 2cents into a variant of “with” to solve this rather than messing with the meaning of if-let:

For example:

var x: MyType?

with x {
  x.someFunction() // okay - this entire block is skipped if “x” is nil anyway
}
x.someFunction() // not okay as-is since x is optional

Using the definition of “with” from Erica’s Method Cascading proposal https://gist.github.com/erica/6794d48d917e2084d6ed, you could even drop the variable reference itself from inside the block body:

with x {
  someFunction() // would call “someFunction()” on “x”
}

And I’d additionally propose that if you wanted to replace if-let-else, you could potentially do this:

if with x {
  // x is known non-nil
} else {
  // x is known to be nil
}
// x is optional and nil-ness is unknown

l8r
Sean

···

On Dec 11, 2015, at 10:28 AM, Marc Knaup via swift-evolution <swift-evolution@swift.org> wrote:

One problem with dropping if/guard let is something like this:

if let x = a?.b.c?.d, y = something(x) {
   …
}

would then have to become this:

let x = a?.b.c?.d
if x != nil {
   let y = something(x)
   if y != nil {
       …
   }
}

I'm fine with
   if let x
as a shortcut for
   if let x = x

It just reads a bit weird - like declaring an immutable variable with no type and no value.

On Fri, Dec 11, 2015 at 5:19 PM, Jeff Kelley via swift-evolution <swift-evolution@swift.org> wrote:
I’ve had similar ideas to this. Instead of ditching the if let syntax altogether, another approach would be to use the existing name if no new name is given, so that this code:

  if let foo = foo { /* use foo */ }

could become this code:

  if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it another name with the if let syntax, that would work as it does today.

Jeff Kelley

SlaunchaMan@gmail.com | @SlaunchaMan | jeffkelley.org

On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution <swift-evolution@swift.org> wrote:

A very common pattern in swift code is to "guard let" or "if let" optionals - this works by creating a new non-optional variable to be used by future code. Often, the new variable is given the same name as the original optional variable, creating a shadow variable. This approach leads to odd looking code like this:

if let nearestX = nearestX { closest = nearestX }
guard let layer = layer else { continue }
// use layer

At a glance, and to Swift newcomers, this code looks completely non-sensical. It's also verbose for simply ensuring the variable is non-nil.

The solution:
Swift should generate unwrapped shadow variables after nil checking. The compiler would treat the below code as if it had created an unwrapped shadow variable.

if nearestX != nil { closest = nearestX } // notice that nearestX isn't force unwrapped
guard layer != nil else { continue }
// use layer, without force unwrapping

Why force unwrapping isn't a good alternative:
You might suggest force unwrapping variables when they're inside an if or after a guard that checks for nil. While this does allow for the "layer = nil" syntax, it results in code that is less resilient to change. Imagine that this code was written:

{code:java}
if layer != nil {
// lots of code before //
layer!.backgroundColor = newColor
// lots of code after //
}
{code}

And much later, you need to use some of the the code in the if body elsewhere, so you copy and paste a huge chunk of it. You likely won't notice the force unwrap, and unless you're lucky, you probably didn't paste it into an if that checked layer for nil. So you get a crash. Because of this, it's important we make safe optional unwrapping as easy and sensical as possible, and minimize the situations that you would need to force unwrap.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Daniel Hooper) #8

I don't think we should discuss "with" here as that is a separate proposal.
I'm trying to draw attention of the approach of Swift generating shadow
variables for you when an optional has been proven to be non-null.

I didn't say we should remove if/guard let. They would stay.

My proposal is to improve readability and reduce verbosity by generating
shadow variables after "x != nil" checks. The "with x {}" and "if let x {}"
approaches only reduce verbosity, at the additional cost of adding new
keywords or syntax to the language, and do not really improve readability.
Due to this, generating a shadow variable after "x != nil" is the most
straightforward, lowest impact and readable approach.

ilya, you bring up a good point about "create shadow->write to
original->read shadow" situations being potentially confusing. However "if
let x = x" is very common in situations that this isn't an issue: taking a
quick look at my code, 50% of all "if/guard let x = x" are on local
variables that cannot possibly change out from under the shadow variable.
If that could lead to confusion in a given situation, you could fall back
on "if let x = maybe_x" to make it clearer that you're operating on a copy
of a variable that won't change out from under you. Alternatively, the "if
x != nil { x.bla() }" syntax could only be allowed on optionals in the
current function scope.

···

On Fri, Dec 11, 2015 at 11:41 AM Sean Heber <sean@fifthace.com> wrote:

This has come up a few times. I’ve thrown my 2cents into a variant of
“with” to solve this rather than messing with the meaning of if-let:

For example:

var x: MyType?

with x {
  x.someFunction() // okay - this entire block is skipped if
“x” is nil anyway
}
x.someFunction() // not okay as-is since x is optional

Using the definition of “with” from Erica’s Method Cascading proposal
https://gist.github.com/erica/6794d48d917e2084d6ed, you could even drop
the variable reference itself from inside the block body:

with x {
  someFunction() // would call “someFunction()” on “x”
}

And I’d additionally propose that if you wanted to replace if-let-else,
you could potentially do this:

if with x {
  // x is known non-nil
} else {
  // x is known to be nil
}
// x is optional and nil-ness is unknown

l8r
Sean

> On Dec 11, 2015, at 10:28 AM, Marc Knaup via swift-evolution < > swift-evolution@swift.org> wrote:
>
> One problem with dropping if/guard let is something like this:
>
> if let x = a?.b.c?.d, y = something(x) {
> …
> }
>
> would then have to become this:
>
> let x = a?.b.c?.d
> if x != nil {
> let y = something(x)
> if y != nil {
> …
> }
> }
>
> I'm fine with
> if let x
> as a shortcut for
> if let x = x
>
> It just reads a bit weird - like declaring an immutable variable with no
type and no value.
>
>
> On Fri, Dec 11, 2015 at 5:19 PM, Jeff Kelley via swift-evolution < > swift-evolution@swift.org> wrote:
> I’ve had similar ideas to this. Instead of ditching the if let syntax
altogether, another approach would be to use the existing name if no new
name is given, so that this code:
>
> if let foo = foo { /* use foo */ }
>
> could become this code:
>
> if let foo { /* use foo */ }
>
> In both cases, foo is non-optional inside the braces. If you gave it
another name with the if let syntax, that would work as it does today.
>
>
> Jeff Kelley
>
> SlaunchaMan@gmail.com | @SlaunchaMan | jeffkelley.org
>
>> On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> A very common pattern in swift code is to "guard let" or "if let"
optionals - this works by creating a new non-optional variable to be used
by future code. Often, the new variable is given the same name as the
original optional variable, creating a shadow variable. This approach leads
to odd looking code like this:
>>
>> if let nearestX = nearestX { closest = nearestX }
>> guard let layer = layer else { continue }
>> // use layer
>>
>> At a glance, and to Swift newcomers, this code looks completely
non-sensical. It's also verbose for simply ensuring the variable is non-nil.
>>
>> The solution:
>> Swift should generate unwrapped shadow variables after nil checking.
The compiler would treat the below code as if it had created an unwrapped
shadow variable.
>>
>> if nearestX != nil { closest = nearestX } // notice that nearestX isn't
force unwrapped
>> guard layer != nil else { continue }
>> // use layer, without force unwrapping
>>
>> Why force unwrapping isn't a good alternative:
>> You might suggest force unwrapping variables when they're inside an if
or after a guard that checks for nil. While this does allow for the "layer
= nil" syntax, it results in code that is less resilient to change. Imagine
that this code was written:
>>
>> {code:java}
>> if layer != nil {
>> // lots of code before //
>> layer!.backgroundColor = newColor
>> // lots of code after //
>> }
>> {code}
>>
>> And much later, you need to use some of the the code in the if body
elsewhere, so you copy and paste a huge chunk of it. You likely won't
notice the force unwrap, and unless you're lucky, you probably didn't paste
it into an if that checked layer for nil. So you get a crash. Because of
this, it's important we make safe optional unwrapping as easy and sensical
as possible, and minimize the situations that you would need to force
unwrap.
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


(Al Skipp) #9

Personally, I wouldn’t like to see any more specialised syntax or compiler voodoo to deal with Optionals. They’re like the Hydra of Swift, though instead of sprouting heads, they sprout new syntax : )

I’d be all for a more generalised approach, ‘do notation’, ‘for comprehensions’, etc – though that’s not likely to happen in the near term (if ever).

···

On 11 Dec 2015, at 17:17, Daniel Hooper via swift-evolution <swift-evolution@swift.org> wrote:

I don't think we should discuss "with" here as that is a separate proposal. I'm trying to draw attention of the approach of Swift generating shadow variables for you when an optional has been proven to be non-null.

I didn't say we should remove if/guard let. They would stay.

My proposal is to improve readability and reduce verbosity by generating shadow variables after "x != nil" checks. The "with x {}" and "if let x {}" approaches only reduce verbosity, at the additional cost of adding new keywords or syntax to the language, and do not really improve readability. Due to this, generating a shadow variable after "x != nil" is the most straightforward, lowest impact and readable approach.

ilya, you bring up a good point about "create shadow->write to original->read shadow" situations being potentially confusing. However "if let x = x" is very common in situations that this isn't an issue: taking a quick look at my code, 50% of all "if/guard let x = x" are on local variables that cannot possibly change out from under the shadow variable. If that could lead to confusion in a given situation, you could fall back on "if let x = maybe_x" to make it clearer that you're operating on a copy of a variable that won't change out from under you. Alternatively, the "if x != nil { x.bla() }" syntax could only be allowed on optionals in the current function scope.


(Marc Knaup) #10

So guard let x = x would become without x or guard with x else? :slight_smile:

Seems weird to have two control statements which do almost the same thing:

   - if (for any conditions) + with (for a subset of things doable with if)
   - guard (for any conditions) + without (for a subset of
   things doable with guard)

Also with/without would have to carry over the where clause which basically
is a if too.

And with multiple assignments Erica's proposal would not work anymore.

As in my previous example (made a bit larger to showcase where):

with x = a?.b.c?.d where !x.isEmpty, y = something(x) {
   // the scope cannot reference to both (x and y) now
}

Btw. there was a rather short discussion
<https://forums.developer.apple.com/thread/5786> in Apple Developer Forums
some time ago too.

···

On Fri, Dec 11, 2015 at 5:40 PM, Sean Heber <sean@fifthace.com> wrote:

This has come up a few times. I’ve thrown my 2cents into a variant of
“with” to solve this rather than messing with the meaning of if-let:

For example:

var x: MyType?

with x {
  x.someFunction() // okay - this entire block is skipped if
“x” is nil anyway
}
x.someFunction() // not okay as-is since x is optional

Using the definition of “with” from Erica’s Method Cascading proposal
https://gist.github.com/erica/6794d48d917e2084d6ed, you could even drop
the variable reference itself from inside the block body:

with x {
  someFunction() // would call “someFunction()” on “x”
}

And I’d additionally propose that if you wanted to replace if-let-else,
you could potentially do this:

if with x {
  // x is known non-nil
} else {
  // x is known to be nil
}
// x is optional and nil-ness is unknown

l8r
Sean

> On Dec 11, 2015, at 10:28 AM, Marc Knaup via swift-evolution < > swift-evolution@swift.org> wrote:
>
> One problem with dropping if/guard let is something like this:
>
> if let x = a?.b.c?.d, y = something(x) {
> …
> }
>
> would then have to become this:
>
> let x = a?.b.c?.d
> if x != nil {
> let y = something(x)
> if y != nil {
> …
> }
> }
>
> I'm fine with
> if let x
> as a shortcut for
> if let x = x
>
> It just reads a bit weird - like declaring an immutable variable with no
type and no value.
>
>
> On Fri, Dec 11, 2015 at 5:19 PM, Jeff Kelley via swift-evolution < > swift-evolution@swift.org> wrote:
> I’ve had similar ideas to this. Instead of ditching the if let syntax
altogether, another approach would be to use the existing name if no new
name is given, so that this code:
>
> if let foo = foo { /* use foo */ }
>
> could become this code:
>
> if let foo { /* use foo */ }
>
> In both cases, foo is non-optional inside the braces. If you gave it
another name with the if let syntax, that would work as it does today.
>
>
> Jeff Kelley
>
> SlaunchaMan@gmail.com | @SlaunchaMan | jeffkelley.org
>
>> On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> A very common pattern in swift code is to "guard let" or "if let"
optionals - this works by creating a new non-optional variable to be used
by future code. Often, the new variable is given the same name as the
original optional variable, creating a shadow variable. This approach leads
to odd looking code like this:
>>
>> if let nearestX = nearestX { closest = nearestX }
>> guard let layer = layer else { continue }
>> // use layer
>>
>> At a glance, and to Swift newcomers, this code looks completely
non-sensical. It's also verbose for simply ensuring the variable is non-nil.
>>
>> The solution:
>> Swift should generate unwrapped shadow variables after nil checking.
The compiler would treat the below code as if it had created an unwrapped
shadow variable.
>>
>> if nearestX != nil { closest = nearestX } // notice that nearestX isn't
force unwrapped
>> guard layer != nil else { continue }
>> // use layer, without force unwrapping
>>
>> Why force unwrapping isn't a good alternative:
>> You might suggest force unwrapping variables when they're inside an if
or after a guard that checks for nil. While this does allow for the "layer
= nil" syntax, it results in code that is less resilient to change. Imagine
that this code was written:
>>
>> {code:java}
>> if layer != nil {
>> // lots of code before //
>> layer!.backgroundColor = newColor
>> // lots of code after //
>> }
>> {code}
>>
>> And much later, you need to use some of the the code in the if body
elsewhere, so you copy and paste a huge chunk of it. You likely won't
notice the force unwrap, and unless you're lucky, you probably didn't paste
it into an if that checked layer for nil. So you get a crash. Because of
this, it's important we make safe optional unwrapping as easy and sensical
as possible, and minimize the situations that you would need to force
unwrap.
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #11

taking a quick look at my code, 50% of all "if/guard let x = x" are on

local variables that cannot possibly change out from under the shadow
variable.

I'm a bit surprised you have a code with non-trivial code paths for both
optional and non-optional values of some computations. Could you perhaps
provide an idiomatic example of such a function?

···

On Fri, Dec 11, 2015 at 8:17 PM, Daniel Hooper via swift-evolution < swift-evolution@swift.org> wrote:

I don't think we should discuss "with" here as that is a separate
proposal. I'm trying to draw attention of the approach of Swift generating
shadow variables for you when an optional has been proven to be non-null.

I didn't say we should remove if/guard let. They would stay.

My proposal is to improve readability and reduce verbosity by generating
shadow variables after "x != nil" checks. The "with x {}" and "if let x {}"
approaches only reduce verbosity, at the additional cost of adding new
keywords or syntax to the language, and do not really improve readability.
Due to this, generating a shadow variable after "x != nil" is the most
straightforward, lowest impact and readable approach.

ilya, you bring up a good point about "create shadow->write to
original->read shadow" situations being potentially confusing. However "if
let x = x" is very common in situations that this isn't an issue: taking a
quick look at my code, 50% of all "if/guard let x = x" are on local
variables that cannot possibly change out from under the shadow variable.
If that could lead to confusion in a given situation, you could fall back
on "if let x = maybe_x" to make it clearer that you're operating on a copy
of a variable that won't change out from under you. Alternatively, the "if
x != nil { x.bla() }" syntax could only be allowed on optionals in the
current function scope.

On Fri, Dec 11, 2015 at 11:41 AM Sean Heber <sean@fifthace.com> wrote:

This has come up a few times. I’ve thrown my 2cents into a variant of
“with” to solve this rather than messing with the meaning of if-let:

For example:

var x: MyType?

with x {
  x.someFunction() // okay - this entire block is skipped if
“x” is nil anyway
}
x.someFunction() // not okay as-is since x is optional

Using the definition of “with” from Erica’s Method Cascading proposal
https://gist.github.com/erica/6794d48d917e2084d6ed, you could even drop
the variable reference itself from inside the block body:

with x {
  someFunction() // would call “someFunction()” on “x”
}

And I’d additionally propose that if you wanted to replace if-let-else,
you could potentially do this:

if with x {
  // x is known non-nil
} else {
  // x is known to be nil
}
// x is optional and nil-ness is unknown

l8r
Sean

> On Dec 11, 2015, at 10:28 AM, Marc Knaup via swift-evolution < >> swift-evolution@swift.org> wrote:
>
> One problem with dropping if/guard let is something like this:
>
> if let x = a?.b.c?.d, y = something(x) {
> …
> }
>
> would then have to become this:
>
> let x = a?.b.c?.d
> if x != nil {
> let y = something(x)
> if y != nil {
> …
> }
> }
>
> I'm fine with
> if let x
> as a shortcut for
> if let x = x
>
> It just reads a bit weird - like declaring an immutable variable with
no type and no value.
>
>
> On Fri, Dec 11, 2015 at 5:19 PM, Jeff Kelley via swift-evolution < >> swift-evolution@swift.org> wrote:
> I’ve had similar ideas to this. Instead of ditching the if let syntax
altogether, another approach would be to use the existing name if no new
name is given, so that this code:
>
> if let foo = foo { /* use foo */ }
>
> could become this code:
>
> if let foo { /* use foo */ }
>
> In both cases, foo is non-optional inside the braces. If you gave it
another name with the if let syntax, that would work as it does today.
>
>
> Jeff Kelley
>
> SlaunchaMan@gmail.com | @SlaunchaMan | jeffkelley.org
>
>> On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution < >> swift-evolution@swift.org> wrote:
>>
>> A very common pattern in swift code is to "guard let" or "if let"
optionals - this works by creating a new non-optional variable to be used
by future code. Often, the new variable is given the same name as the
original optional variable, creating a shadow variable. This approach leads
to odd looking code like this:
>>
>> if let nearestX = nearestX { closest = nearestX }
>> guard let layer = layer else { continue }
>> // use layer
>>
>> At a glance, and to Swift newcomers, this code looks completely
non-sensical. It's also verbose for simply ensuring the variable is non-nil.
>>
>> The solution:
>> Swift should generate unwrapped shadow variables after nil checking.
The compiler would treat the below code as if it had created an unwrapped
shadow variable.
>>
>> if nearestX != nil { closest = nearestX } // notice that nearestX
isn't force unwrapped
>> guard layer != nil else { continue }
>> // use layer, without force unwrapping
>>
>> Why force unwrapping isn't a good alternative:
>> You might suggest force unwrapping variables when they're inside an if
or after a guard that checks for nil. While this does allow for the "layer
= nil" syntax, it results in code that is less resilient to change. Imagine
that this code was written:
>>
>> {code:java}
>> if layer != nil {
>> // lots of code before //
>> layer!.backgroundColor = newColor
>> // lots of code after //
>> }
>> {code}
>>
>> And much later, you need to use some of the the code in the if body
elsewhere, so you copy and paste a huge chunk of it. You likely won't
notice the force unwrap, and unless you're lucky, you probably didn't paste
it into an if that checked layer for nil. So you get a crash. Because of
this, it's important we make safe optional unwrapping as easy and sensical
as possible, and minimize the situations that you would need to force
unwrap.
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Kevin Wooten) #12

As much fun as it to example with foo, I would argue the opposite when you use some real world variable names:

if let someInterestingViewConroller = someInterestingViewConroller {
}

vs

If let someInterestingViewConroller {
}

We know what let does and it should be enough to impart the necessary information for this statement.

When it comes to newcomers I think you'd be hard pressed to find somebody who'd be able to understand either form without teaching; so not losing much there.

···

On Dec 19, 2015, at 10:01 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 11, 2015, at 8:19 AM, Jeff Kelley via swift-evolution <swift-evolution@swift.org> wrote:

I’ve had similar ideas to this. Instead of ditching the if let syntax altogether, another approach would be to use the existing name if no new name is given, so that this code:

  if let foo = foo { /* use foo */ }

could become this code:

  if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it another name with the if let syntax, that would work as it does today.

Hi Jeff,

This is commonly requested - the problem is that while it does help reduce boilerplate, it runs counter to the goal of improving clarity.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Sean Heber) #13

I would not have “without” - only “with”. If you wanted to put it into a guard, I’d go with guard-with. If the *only* intent of the “with" keyword was to unwrap-in-place (basically), then I’d imagine it would *only* work inside an if or guard and not stand on its own. If it was combined with Erica’s proposal, I think it could stand on its own, though.

with x = a?.b.c?.d where !x.isEmpty, y = something(x) {
   // the scope cannot reference to both (x and y) now
}

If you’re declaring “x” for the first time, you’d have to have a let/var in there.

with let x = <potentially optional expression>, y = <another optional expression> where <cond> {}

l8r
Sean

···

On Dec 11, 2015, at 10:52 AM, Marc Knaup <marc@knaup.koeln> wrote:

So guard let x = x would become without x or guard with x else? :slight_smile:

Seems weird to have two control statements which do almost the same thing:
  • if (for any conditions) + with (for a subset of things doable with if)
  • guard (for any conditions) + without (for a subset of things doable with guard)
Also with/without would have to carry over the where clause which basically is a if too.

And with multiple assignments Erica's proposal would not work anymore.

As in my previous example (made a bit larger to showcase where):

Btw. there was a rather short discussion in Apple Developer Forums some time ago too.

On Fri, Dec 11, 2015 at 5:40 PM, Sean Heber <sean@fifthace.com> wrote:
This has come up a few times. I’ve thrown my 2cents into a variant of “with” to solve this rather than messing with the meaning of if-let:

For example:

var x: MyType?

with x {
  x.someFunction() // okay - this entire block is skipped if “x” is nil anyway
}
x.someFunction() // not okay as-is since x is optional

Using the definition of “with” from Erica’s Method Cascading proposal https://gist.github.com/erica/6794d48d917e2084d6ed, you could even drop the variable reference itself from inside the block body:

with x {
  someFunction() // would call “someFunction()” on “x”
}

And I’d additionally propose that if you wanted to replace if-let-else, you could potentially do this:

if with x {
  // x is known non-nil
} else {
  // x is known to be nil
}
// x is optional and nil-ness is unknown

l8r
Sean

> On Dec 11, 2015, at 10:28 AM, Marc Knaup via swift-evolution <swift-evolution@swift.org> wrote:
>
> One problem with dropping if/guard let is something like this:
>
> if let x = a?.b.c?.d, y = something(x) {
> …
> }
>
> would then have to become this:
>
> let x = a?.b.c?.d
> if x != nil {
> let y = something(x)
> if y != nil {
> …
> }
> }
>
> I'm fine with
> if let x
> as a shortcut for
> if let x = x
>
> It just reads a bit weird - like declaring an immutable variable with no type and no value.
>
>
> On Fri, Dec 11, 2015 at 5:19 PM, Jeff Kelley via swift-evolution <swift-evolution@swift.org> wrote:
> I’ve had similar ideas to this. Instead of ditching the if let syntax altogether, another approach would be to use the existing name if no new name is given, so that this code:
>
> if let foo = foo { /* use foo */ }
>
> could become this code:
>
> if let foo { /* use foo */ }
>
> In both cases, foo is non-optional inside the braces. If you gave it another name with the if let syntax, that would work as it does today.
>
>
> Jeff Kelley
>
> SlaunchaMan@gmail.com | @SlaunchaMan | jeffkelley.org
>
>> On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution <swift-evolution@swift.org> wrote:
>>
>> A very common pattern in swift code is to "guard let" or "if let" optionals - this works by creating a new non-optional variable to be used by future code. Often, the new variable is given the same name as the original optional variable, creating a shadow variable. This approach leads to odd looking code like this:
>>
>> if let nearestX = nearestX { closest = nearestX }
>> guard let layer = layer else { continue }
>> // use layer
>>
>> At a glance, and to Swift newcomers, this code looks completely non-sensical. It's also verbose for simply ensuring the variable is non-nil.
>>
>> The solution:
>> Swift should generate unwrapped shadow variables after nil checking. The compiler would treat the below code as if it had created an unwrapped shadow variable.
>>
>> if nearestX != nil { closest = nearestX } // notice that nearestX isn't force unwrapped
>> guard layer != nil else { continue }
>> // use layer, without force unwrapping
>>
>> Why force unwrapping isn't a good alternative:
>> You might suggest force unwrapping variables when they're inside an if or after a guard that checks for nil. While this does allow for the "layer = nil" syntax, it results in code that is less resilient to change. Imagine that this code was written:
>>
>> {code:java}
>> if layer != nil {
>> // lots of code before //
>> layer!.backgroundColor = newColor
>> // lots of code after //
>> }
>> {code}
>>
>> And much later, you need to use some of the the code in the if body elsewhere, so you copy and paste a huge chunk of it. You likely won't notice the force unwrap, and unless you're lucky, you probably didn't paste it into an if that checked layer for nil. So you get a crash. Because of this, it's important we make safe optional unwrapping as easy and sensical as possible, and minimize the situations that you would need to force unwrap.
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


(Daniel Hooper) #14

Comes up all the time in swift code that interfaces with objective-c. For
safety all object parameters should be optional since objective-c can't
make strong guarantees about nullability.

for example:

extension SomeObjcClass {

func selectLayer(layer: PRLayer?) {

        guard let layer = layer else { return }

// do things with layer

}

}

···

On Fri, Dec 11, 2015 at 12:30 PM ilya <ilya.nikokoshev@gmail.com> wrote:

> taking a quick look at my code, 50% of all "if/guard let x = x" are on
local variables that cannot possibly change out from under the shadow
variable.

I'm a bit surprised you have a code with non-trivial code paths for both
optional and non-optional values of some computations. Could you perhaps
provide an idiomatic example of such a function?

On Fri, Dec 11, 2015 at 8:17 PM, Daniel Hooper via swift-evolution < > swift-evolution@swift.org> wrote:

I don't think we should discuss "with" here as that is a separate
proposal. I'm trying to draw attention of the approach of Swift generating
shadow variables for you when an optional has been proven to be non-null.

I didn't say we should remove if/guard let. They would stay.

My proposal is to improve readability and reduce verbosity by generating
shadow variables after "x != nil" checks. The "with x {}" and "if let x {}"
approaches only reduce verbosity, at the additional cost of adding new
keywords or syntax to the language, and do not really improve readability.
Due to this, generating a shadow variable after "x != nil" is the most
straightforward, lowest impact and readable approach.

ilya, you bring up a good point about "create shadow->write to
original->read shadow" situations being potentially confusing. However "if
let x = x" is very common in situations that this isn't an issue: taking a
quick look at my code, 50% of all "if/guard let x = x" are on local
variables that cannot possibly change out from under the shadow variable.
If that could lead to confusion in a given situation, you could fall back
on "if let x = maybe_x" to make it clearer that you're operating on a copy
of a variable that won't change out from under you. Alternatively, the "if
x != nil { x.bla() }" syntax could only be allowed on optionals in the
current function scope.

On Fri, Dec 11, 2015 at 11:41 AM Sean Heber <sean@fifthace.com> wrote:

This has come up a few times. I’ve thrown my 2cents into a variant of
“with” to solve this rather than messing with the meaning of if-let:

For example:

var x: MyType?

with x {
  x.someFunction() // okay - this entire block is skipped
if “x” is nil anyway
}
x.someFunction() // not okay as-is since x is optional

Using the definition of “with” from Erica’s Method Cascading proposal
https://gist.github.com/erica/6794d48d917e2084d6ed, you could even drop
the variable reference itself from inside the block body:

with x {
  someFunction() // would call “someFunction()” on “x”
}

And I’d additionally propose that if you wanted to replace if-let-else,
you could potentially do this:

if with x {
  // x is known non-nil
} else {
  // x is known to be nil
}
// x is optional and nil-ness is unknown

l8r
Sean

> On Dec 11, 2015, at 10:28 AM, Marc Knaup via swift-evolution < >>> swift-evolution@swift.org> wrote:
>
> One problem with dropping if/guard let is something like this:
>
> if let x = a?.b.c?.d, y = something(x) {
> …
> }
>
> would then have to become this:
>
> let x = a?.b.c?.d
> if x != nil {
> let y = something(x)
> if y != nil {
> …
> }
> }
>
> I'm fine with
> if let x
> as a shortcut for
> if let x = x
>
> It just reads a bit weird - like declaring an immutable variable with
no type and no value.
>
>
> On Fri, Dec 11, 2015 at 5:19 PM, Jeff Kelley via swift-evolution < >>> swift-evolution@swift.org> wrote:
> I’ve had similar ideas to this. Instead of ditching the if let syntax
altogether, another approach would be to use the existing name if no new
name is given, so that this code:
>
> if let foo = foo { /* use foo */ }
>
> could become this code:
>
> if let foo { /* use foo */ }
>
> In both cases, foo is non-optional inside the braces. If you gave it
another name with the if let syntax, that would work as it does today.
>
>
> Jeff Kelley
>
> SlaunchaMan@gmail.com | @SlaunchaMan | jeffkelley.org
>
>> On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution < >>> swift-evolution@swift.org> wrote:
>>
>> A very common pattern in swift code is to "guard let" or "if let"
optionals - this works by creating a new non-optional variable to be used
by future code. Often, the new variable is given the same name as the
original optional variable, creating a shadow variable. This approach leads
to odd looking code like this:
>>
>> if let nearestX = nearestX { closest = nearestX }
>> guard let layer = layer else { continue }
>> // use layer
>>
>> At a glance, and to Swift newcomers, this code looks completely
non-sensical. It's also verbose for simply ensuring the variable is non-nil.
>>
>> The solution:
>> Swift should generate unwrapped shadow variables after nil checking.
The compiler would treat the below code as if it had created an unwrapped
shadow variable.
>>
>> if nearestX != nil { closest = nearestX } // notice that nearestX
isn't force unwrapped
>> guard layer != nil else { continue }
>> // use layer, without force unwrapping
>>
>> Why force unwrapping isn't a good alternative:
>> You might suggest force unwrapping variables when they're inside an
if or after a guard that checks for nil. While this does allow for the
"layer = nil" syntax, it results in code that is less resilient to change.
Imagine that this code was written:
>>
>> {code:java}
>> if layer != nil {
>> // lots of code before //
>> layer!.backgroundColor = newColor
>> // lots of code after //
>> }
>> {code}
>>
>> And much later, you need to use some of the the code in the if body
elsewhere, so you copy and paste a huge chunk of it. You likely won't
notice the force unwrap, and unless you're lucky, you probably didn't paste
it into an if that checked layer for nil. So you get a crash. Because of
this, it's important we make safe optional unwrapping as easy and sensical
as possible, and minimize the situations that you would need to force
unwrap.
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Marc Knaup) #15

It would only work well for local variables which were not captured by
reference by a closure.
Anything else will either lead to race conditions or be confusing when a
variable suddenly becomes out of sync without the user copying the value to
another variable explicitly.

There doesn't have to be a shadow variable though.
When the compiler supports nil-analyis it does know when an optional is no
longer optional - and - it will also know when the variable was changed
locally and is now probably nil again.

var x: String?
if x == nil {
   return
}
// x is now non-optional

x = nil
// x is optional again

In any case this will work only for very simple if conditions.

···

On Fri, Dec 11, 2015 at 6:30 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

> taking a quick look at my code, 50% of all "if/guard let x = x" are on
local variables that cannot possibly change out from under the shadow
variable.

I'm a bit surprised you have a code with non-trivial code paths for both
optional and non-optional values of some computations. Could you perhaps
provide an idiomatic example of such a function?

On Fri, Dec 11, 2015 at 8:17 PM, Daniel Hooper via swift-evolution < > swift-evolution@swift.org> wrote:

I don't think we should discuss "with" here as that is a separate
proposal. I'm trying to draw attention of the approach of Swift generating
shadow variables for you when an optional has been proven to be non-null.

I didn't say we should remove if/guard let. They would stay.

My proposal is to improve readability and reduce verbosity by generating
shadow variables after "x != nil" checks. The "with x {}" and "if let x {}"
approaches only reduce verbosity, at the additional cost of adding new
keywords or syntax to the language, and do not really improve readability.
Due to this, generating a shadow variable after "x != nil" is the most
straightforward, lowest impact and readable approach.

ilya, you bring up a good point about "create shadow->write to
original->read shadow" situations being potentially confusing. However "if
let x = x" is very common in situations that this isn't an issue: taking a
quick look at my code, 50% of all "if/guard let x = x" are on local
variables that cannot possibly change out from under the shadow variable.
If that could lead to confusion in a given situation, you could fall back
on "if let x = maybe_x" to make it clearer that you're operating on a copy
of a variable that won't change out from under you. Alternatively, the "if
x != nil { x.bla() }" syntax could only be allowed on optionals in the
current function scope.

On Fri, Dec 11, 2015 at 11:41 AM Sean Heber <sean@fifthace.com> wrote:

This has come up a few times. I’ve thrown my 2cents into a variant of
“with” to solve this rather than messing with the meaning of if-let:

For example:

var x: MyType?

with x {
  x.someFunction() // okay - this entire block is skipped
if “x” is nil anyway
}
x.someFunction() // not okay as-is since x is optional

Using the definition of “with” from Erica’s Method Cascading proposal
https://gist.github.com/erica/6794d48d917e2084d6ed, you could even drop
the variable reference itself from inside the block body:

with x {
  someFunction() // would call “someFunction()” on “x”
}

And I’d additionally propose that if you wanted to replace if-let-else,
you could potentially do this:

if with x {
  // x is known non-nil
} else {
  // x is known to be nil
}
// x is optional and nil-ness is unknown

l8r
Sean

> On Dec 11, 2015, at 10:28 AM, Marc Knaup via swift-evolution < >>> swift-evolution@swift.org> wrote:
>
> One problem with dropping if/guard let is something like this:
>
> if let x = a?.b.c?.d, y = something(x) {
> …
> }
>
> would then have to become this:
>
> let x = a?.b.c?.d
> if x != nil {
> let y = something(x)
> if y != nil {
> …
> }
> }
>
> I'm fine with
> if let x
> as a shortcut for
> if let x = x
>
> It just reads a bit weird - like declaring an immutable variable with
no type and no value.
>
>
> On Fri, Dec 11, 2015 at 5:19 PM, Jeff Kelley via swift-evolution < >>> swift-evolution@swift.org> wrote:
> I’ve had similar ideas to this. Instead of ditching the if let syntax
altogether, another approach would be to use the existing name if no new
name is given, so that this code:
>
> if let foo = foo { /* use foo */ }
>
> could become this code:
>
> if let foo { /* use foo */ }
>
> In both cases, foo is non-optional inside the braces. If you gave it
another name with the if let syntax, that would work as it does today.
>
>
> Jeff Kelley
>
> SlaunchaMan@gmail.com | @SlaunchaMan | jeffkelley.org
>
>> On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution < >>> swift-evolution@swift.org> wrote:
>>
>> A very common pattern in swift code is to "guard let" or "if let"
optionals - this works by creating a new non-optional variable to be used
by future code. Often, the new variable is given the same name as the
original optional variable, creating a shadow variable. This approach leads
to odd looking code like this:
>>
>> if let nearestX = nearestX { closest = nearestX }
>> guard let layer = layer else { continue }
>> // use layer
>>
>> At a glance, and to Swift newcomers, this code looks completely
non-sensical. It's also verbose for simply ensuring the variable is non-nil.
>>
>> The solution:
>> Swift should generate unwrapped shadow variables after nil checking.
The compiler would treat the below code as if it had created an unwrapped
shadow variable.
>>
>> if nearestX != nil { closest = nearestX } // notice that nearestX
isn't force unwrapped
>> guard layer != nil else { continue }
>> // use layer, without force unwrapping
>>
>> Why force unwrapping isn't a good alternative:
>> You might suggest force unwrapping variables when they're inside an
if or after a guard that checks for nil. While this does allow for the
"layer = nil" syntax, it results in code that is less resilient to change.
Imagine that this code was written:
>>
>> {code:java}
>> if layer != nil {
>> // lots of code before //
>> layer!.backgroundColor = newColor
>> // lots of code after //
>> }
>> {code}
>>
>> And much later, you need to use some of the the code in the if body
elsewhere, so you copy and paste a huge chunk of it. You likely won't
notice the force unwrap, and unless you're lucky, you probably didn't paste
it into an if that checked layer for nil. So you get a crash. Because of
this, it's important we make safe optional unwrapping as easy and sensical
as possible, and minimize the situations that you would need to force
unwrap.
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dennis Lysenko) #16

What if we made the keyword "unwrap"?

if unwrap someViewController {
// now there is a shadowing nonoptional (unwrapped) variable of the same
name only within this scope, boiling down to simple syntactic sugar for
optional binding and it is fairly clear.
}

···

On Sat, Dec 19, 2015, 1:31 PM Kevin Wooten via swift-evolution < swift-evolution@swift.org> wrote:

As much fun as it to example with foo, I would argue the opposite when you
use some real world variable names:

if let someInterestingViewConroller = someInterestingViewConroller {
}

vs

If let someInterestingViewConroller {
}

We know what let does and it should be enough to impart the necessary
information for this statement.

When it comes to newcomers I think you'd be hard pressed to find somebody
who'd be able to understand either form without teaching; so not losing
much there.

On Dec 19, 2015, at 10:01 AM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

On Dec 11, 2015, at 8:19 AM, Jeff Kelley via swift-evolution < > swift-evolution@swift.org> wrote:

I’ve had similar ideas to this. Instead of ditching the if let syntax
altogether, another approach would be to use the existing name if no new
name is given, so that this code:

if let foo = foo { /* use foo */ }

could become this code:

if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it
another name with the if let syntax, that would work as it does today.

Hi Jeff,

This is commonly requested - the problem is that while it does help reduce
boilerplate, it runs counter to the goal of improving clarity.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(ilya) #17

I prefer

if let vc = someInterestingViewConroller {
vc.doSomething()
}

- Explicit is better than implicit
- shadowing is bad
- now there's no ambiguity about how to change the original property.

Therefore I'm -1 on any proposal that hides explicit name binding and/or
increases shadowing, including let foo and unwrap foo.

···

On Sat, Dec 19, 2015 at 21:31 Kevin Wooten via swift-evolution < swift-evolution@swift.org> wrote:

As much fun as it to example with foo, I would argue the opposite when you
use some real world variable names:

if let someInterestingViewConroller = someInterestingViewConroller {
}

vs

If let someInterestingViewConroller {
}

We know what let does and it should be enough to impart the necessary
information for this statement.

When it comes to newcomers I think you'd be hard pressed to find somebody
who'd be able to understand either form without teaching; so not losing
much there.

On Dec 19, 2015, at 10:01 AM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

On Dec 11, 2015, at 8:19 AM, Jeff Kelley via swift-evolution < > swift-evolution@swift.org> wrote:

I’ve had similar ideas to this. Instead of ditching the if let syntax
altogether, another approach would be to use the existing name if no new
name is given, so that this code:

if let foo = foo { /* use foo */ }

could become this code:

if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it
another name with the if let syntax, that would work as it does today.

Hi Jeff,

This is commonly requested - the problem is that while it does help reduce
boilerplate, it runs counter to the goal of improving clarity.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #18

I prefer

if let vc = someInterestingViewConroller {
  vc.doSomething()
}

- Explicit is better than implicit
- shadowing is bad
- now there's no ambiguity about how to change the original property.

Therefore I'm -1 on any proposal that hides explicit name binding and/or increases shadowing, including let foo and unwrap foo.

+1 to that. IMO re-using the same name for the unwrapped version of an optional variable does not help readability, and I don't want to encourage it. In a localized context like this, a short name is often better anyway as it declutters the code inside the block.

As much fun as it to example with foo, I would argue the opposite when you use some real world variable names:

if let someInterestingViewConroller = someInterestingViewConroller {
}

vs

If let someInterestingViewConroller {
}

We know what let does and it should be enough to impart the necessary information for this statement.

When it comes to newcomers I think you'd be hard pressed to find somebody who'd be able to understand either form without teaching; so not losing much there.

I’ve had similar ideas to this. Instead of ditching the if let syntax altogether, another approach would be to use the existing name if no new name is given, so that this code:

  if let foo = foo { /* use foo */ }

could become this code:

  if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it another name with the if let syntax, that would work as it does today.

Hi Jeff,

This is commonly requested - the problem is that while it does help reduce boilerplate, it runs counter to the goal of improving clarity.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

-Dave

···

On Dec 19, 2015, at 12:37 PM, ilya via swift-evolution <swift-evolution@swift.org> wrote:
On Sat, Dec 19, 2015 at 21:31 Kevin Wooten via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Dec 19, 2015, at 10:01 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 11, 2015, at 8:19 AM, Jeff Kelley via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:


(Kevin Wooten) #19

I prefer

if let vc = someInterestingViewConroller {
  vc.doSomething()
}

- Explicit is better than implicit
- shadowing is bad
- now there's no ambiguity about how to change the original property.

Creating a less descriptive short name in place of usually more descriptive name, which is already used throughout your code, is not “more explicit”. You’ve removed shadowing and instead created two separately name variables referring to the same value. You’ve created _more_ ambiguity, not less.

Also, for 1 line blocks of code, “vc” might be ok but blocks 20 lines long that contain other contrived names, just to remove shadowing, (e.g. “vc2”, using your example) is only going to make it worse.

···

On Dec 19, 2015, at 1:37 PM, ilya <ilya.nikokoshev@gmail.com> wrote:

Therefore I'm -1 on any proposal that hides explicit name binding and/or increases shadowing, including let foo and unwrap foo.

On Sat, Dec 19, 2015 at 21:31 Kevin Wooten via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
As much fun as it to example with foo, I would argue the opposite when you use some real world variable names:

if let someInterestingViewConroller = someInterestingViewConroller {
}

vs

If let someInterestingViewConroller {
}

We know what let does and it should be enough to impart the necessary information for this statement.

When it comes to newcomers I think you'd be hard pressed to find somebody who'd be able to understand either form without teaching; so not losing much there.

On Dec 19, 2015, at 10:01 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 11, 2015, at 8:19 AM, Jeff Kelley via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I’ve had similar ideas to this. Instead of ditching the if let syntax altogether, another approach would be to use the existing name if no new name is given, so that this code:

  if let foo = foo { /* use foo */ }

could become this code:

  if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it another name with the if let syntax, that would work as it does today.

Hi Jeff,

This is commonly requested - the problem is that while it does help reduce boilerplate, it runs counter to the goal of improving clarity.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dennis Lysenko) #20

Disagree. Short names are less descriptive and less readable. And saying
"shadowing is bad" without any argument is just silly for a mailing list.

···

On Sat, Dec 19, 2015, 3:57 PM Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 19, 2015, at 12:37 PM, ilya via swift-evolution < > swift-evolution@swift.org> wrote:

I prefer

if let vc = someInterestingViewConroller {
vc.doSomething()
}

- Explicit is better than implicit
- shadowing is bad
- now there's no ambiguity about how to change the original property.

Therefore I'm -1 on any proposal that hides explicit name binding and/or
increases shadowing, including let foo and unwrap foo.

+1 to that. IMO re-using the same name for the unwrapped version of an
optional variable does not help readability, and I don't want to encourage
it. In a localized context like this, a short name is often better anyway
as it declutters the code inside the block.

On Sat, Dec 19, 2015 at 21:31 Kevin Wooten via swift-evolution < > swift-evolution@swift.org> wrote:

As much fun as it to example with foo, I would argue the opposite when
you use some real world variable names:

if let someInterestingViewConroller = someInterestingViewConroller {
}

vs

If let someInterestingViewConroller {
}

We know what let does and it should be enough to impart the necessary
information for this statement.

When it comes to newcomers I think you'd be hard pressed to find somebody
who'd be able to understand either form without teaching; so not losing
much there.

On Dec 19, 2015, at 10:01 AM, Chris Lattner via swift-evolution < >> swift-evolution@swift.org> wrote:

On Dec 11, 2015, at 8:19 AM, Jeff Kelley via swift-evolution < >> swift-evolution@swift.org> wrote:

I’ve had similar ideas to this. Instead of ditching the if let syntax
altogether, another approach would be to use the existing name if no new
name is given, so that this code:

if let foo = foo { /* use foo */ }

could become this code:

if let foo { /* use foo */ }

In both cases, foo is non-optional inside the braces. If you gave it
another name with the if let syntax, that would work as it does today.

Hi Jeff,

This is commonly requested - the problem is that while it does help
reduce boilerplate, it runs counter to the goal of improving clarity.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

-Dave

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution