[swift-evolution] Proposal Sketch: simplify optional unwrapping syntax

Marc Knaup marc at knaup.koeln
Fri Dec 11 11:32:27 CST 2015


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 at 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 at 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 at 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 at 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 at 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 at gmail.com | @SlaunchaMan | jeffkelley.org
>>> >
>>> >> On Dec 11, 2015, at 11:11 AM, Daniel Hooper via swift-evolution <
>>> swift-evolution at 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 at swift.org
>>> >> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> >
>>> >
>>> >
>>> > _______________________________________________
>>> > swift-evolution mailing list
>>> > swift-evolution at swift.org
>>> > https://lists.swift.org/mailman/listinfo/swift-evolution
>>> >
>>> >
>>> >  _______________________________________________
>>> > swift-evolution mailing list
>>> > swift-evolution at swift.org
>>> > https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151211/0208b833/attachment.html>


More information about the swift-evolution mailing list