Auto Unwrapping Of Optionals


(Tod Cunningham) #1

I'm new to the swift evolution community, but I wanted to toss an idea out there to get some feedback on it. So here it goes...

Currently, if you assign a non-nil value to an optional and then want to access that optional later, in the same context, you need to manually unwrap the value. This is usually done either by using "!" or by using something like "if let" or guard.

What would it be like if the compiler could auto unwrap, in cases where in knows the optional will have some value? This would make the code "clean" and still be safe.

This concept of Auto Unwrapping of Optionals is similar to Implicitly Unwrapped Optionals, but is only applied when the compiler knows it is safe to do so.

Take the following example:

class Test {
    var today: NSDate? = nil
    func test() {
        today = today ?? NSDate()
        print("Today is \(today)") // Would be printed as an optional
        let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // Requires ! or (if let) to unwrap
        // ... do stuff with timeInterval ...
    }
}

With the above example, the compiler could known that today has a value after it's set in the test method. So why couldn't the compiler auto unwrap it when accessed? This would mean manual unwrapping would be unnecessary:

class Test {
    var today: NSDate? = nil
    func test() {
        today = today ?? NSDate()
        print("Today is \(today)") // Would be printed as a value (not an optional)
        let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
        // ... do stuff with timeInterval ...
    }
}

If the value later gets set to an optional value, then it will no longer be auto unwrapable :

class Test {
    var today: NSDate? = nil

    func optionalDay() -> NSDate? {
        return NSDate()
    }

    func test() {
        today = today ?? NSDate()
        print("Today is \(today)") // Would be printed as a value (not an optional)
        let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
        let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed

        // If today is assigned an optional value, we can no longer auto unwrap it
        today = optionalDay()
        print("Today is \(today)") // Would be printed as an optional
        let timeInterval3: NSTimeInterval = today!.timeIntervalSinceNow // manual unwrapping would be required
    }
}

Note in the above example, explicit unwrapping would still be allow. The variable is still an optional. This allows for existing code to remain unchanged.

This change would encourage less use of forced unwrapping "!", generally require the developer to write less code, and would maintain code safety. On the down side, it is performing some compiler “magic”. It would be yet another thing to explain when trying to introduce people to swift and especially optionals.

What do you all think, would something like this be worth pursuing, what other pluses or minus would this introduce, has something like this already been discussed?

Thanks,
Tod Cunningham


Introducing `Unwrappable`, a biased unwrapping protocol
(Haravikk) #2

This is definitely something I’m hoping to see as well, alongside more intelligent handling of the is keyword, as currently Swift doesn’t handle the following either:

  if foo is SomeType { /* foo could be safely used as SomeType here, but currently is not */ }

Hopefully someone more familiar can weigh in, as it seems like something I expect to be on the way but perhaps has been delayed in case any further changes to the type system were required?

···

On 29 Apr 2016, at 15:37, Tod Cunningham via swift-evolution <swift-evolution@swift.org> wrote:

I'm new to the swift evolution community, but I wanted to toss an idea out there to get some feedback on it. So here it goes...

Currently, if you assign a non-nil value to an optional and then want to access that optional later, in the same context, you need to manually unwrap the value. This is usually done either by using "!" or by using something like "if let" or guard.

What would it be like if the compiler could auto unwrap, in cases where in knows the optional will have some value? This would make the code "clean" and still be safe.

This concept of Auto Unwrapping of Optionals is similar to Implicitly Unwrapped Optionals, but is only applied when the compiler knows it is safe to do so.

Take the following example:

class Test {
   var today: NSDate? = nil
   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as an optional
       let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // Requires ! or (if let) to unwrap
       // ... do stuff with timeInterval ...
   }
}

With the above example, the compiler could known that today has a value after it's set in the test method. So why couldn't the compiler auto unwrap it when accessed? This would mean manual unwrapping would be unnecessary:

class Test {
   var today: NSDate? = nil
   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as a value (not an optional)
       let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
       // ... do stuff with timeInterval ...
   }
}

If the value later gets set to an optional value, then it will no longer be auto unwrapable :

class Test {
   var today: NSDate? = nil

   func optionalDay() -> NSDate? {
       return NSDate()
   }

   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as a value (not an optional)
       let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
       let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed

       // If today is assigned an optional value, we can no longer auto unwrap it
       today = optionalDay()
       print("Today is \(today)") // Would be printed as an optional
       let timeInterval3: NSTimeInterval = today!.timeIntervalSinceNow // manual unwrapping would be required
   }
}

Note in the above example, explicit unwrapping would still be allow. The variable is still an optional. This allows for existing code to remain unchanged.

This change would encourage less use of forced unwrapping "!", generally require the developer to write less code, and would maintain code safety. On the down side, it is performing some compiler “magic”. It would be yet another thing to explain when trying to introduce people to swift and especially optionals.

What do you all think, would something like this be worth pursuing, what other pluses or minus would this introduce, has something like this already been discussed?

Thanks,
Tod Cunningham

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


(Adrian Zubarev) #3

+1 But your example is too good to be true. :slight_smile:

What would happen to this code:

class A {

var value: Type? = nil

func reset() { self.value = nil }

func test() {

self.value = self.value ?? Type()

self.reset()

self.value.doSomething()

// can the compiler be sure that our value wasn't reset somewhere from a different scope ?
}
}

I'm curious what will happen here. Can someone clarify on that?

···

--
Adrian Zubarev

Am 29. April 2016 um 16:37:37, Tod Cunningham via swift-evolution (swift-evolution@swift.org(mailto:swift-evolution@swift.org)) schrieb:

I'm new to the swift evolution community, but I wanted to toss an idea out there to get some feedback on it. So here it goes...

Currently, if you assign a non-nil value to an optional and then want to access that optional later, in the same context, you need to manually unwrap the value. This is usually done either by using "!" or by using something like "if let" or guard.

What would it be like if the compiler could auto unwrap, in cases where in knows the optional will have some value? This would make the code "clean" and still be safe.

This concept of Auto Unwrapping of Optionals is similar to Implicitly Unwrapped Optionals, but is only applied when the compiler knows it is safe to do so.

Take the following example:

class Test {
var today: NSDate? = nil
func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as an optional
let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // Requires ! or (if let) to unwrap
// ... do stuff with timeInterval ...
}
}

With the above example, the compiler could known that today has a value after it's set in the test method. So why couldn't the compiler auto unwrap it when accessed? This would mean manual unwrapping would be unnecessary:

class Test {
var today: NSDate? = nil
func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as a value (not an optional)
let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
// ... do stuff with timeInterval ...
}
}

If the value later gets set to an optional value, then it will no longer be auto unwrapable :

class Test {
var today: NSDate? = nil

func optionalDay() -> NSDate? {
return NSDate()
}

func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as a value (not an optional)
let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed

// If today is assigned an optional value, we can no longer auto unwrap it
today = optionalDay()
print("Today is \(today)") // Would be printed as an optional
let timeInterval3: NSTimeInterval = today!.timeIntervalSinceNow // manual unwrapping would be required
}
}

Note in the above example, explicit unwrapping would still be allow. The variable is still an optional. This allows for existing code to remain unchanged.

This change would encourage less use of forced unwrapping "!", generally require the developer to write less code, and would maintain code safety. On the down side, it is performing some compiler “magic”. It would be yet another thing to explain when trying to introduce people to swift and especially optionals.

What do you all think, would something like this be worth pursuing, what other pluses or minus would this introduce, has something like this already been discussed?

Thanks,
Tod Cunningham

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


(Michael Peternell) #4

I thought about this too, and I think it would be convenient. It feels a bit like artificial intelligence though, and I'm not sure how easy it would be to implement this. It should be sharply defined what the compiler should infer and what not. This can be very hard.

For example, you write `print("Today is \(today)")`: should it print an Optional or should it print just the date, because it knows that it cannot be nil? I say it should print `.Optional(... 23:37)`. But what if it prints just the date? In this case `today` cannot be nil, so it would be safe to just print `23:37`. Now, I see the source code example for the first time. What thinking process do I have to go through to know if the variable is automatically unwrapped or not? I'll think "can this variable be nil at that place?" The compiler also thinks "can this variable be nil at that place?" Will the compiler and me always agree in this respect? Or do I have to think "Will the compiler think that the variable can be nil at this place? Can it decide that it can never be nil?"? In this case, the language user needs to have a mental model about what inferences the compiler is able to do, so the rules must be fixed and easily understandable. The model would probably be something along the lines of "NSDate() always returns a non-optional value, so `x ?? NSDate()` is non-optional too." But knowing that foo(3) will return a value because foo only returns nil if its argument is < 0 is probably out of scope, even if the foo-function is defined within the same source file and even if the `-O2` flag is turned on (because you don't want to have different semantics depending on how much optimization is turned on).

Therefore, I think it would be best to only apply implicit unwrapping in cases where the code would otherwise not compile.

E.g. now you have to write

    var x: Int? = someFunction()
    if(x == nil) { return 22 }
    return x!+2+(2*x!);

It should be possible to rewrite this as

    var x: Int? = someFunction()
    if(x == nil) { return 22 }
    return x+2+(2*x);

This should only work for variables defined within the same scope, so this should fail to compile:

    // type of self.foo is String
    if(self.foo == nil) { return 11 }
    // type of myFunc is String -> ()
    myFunc(self.foo)
  
because even though self.foo was non-nil 1 microsecond ago, it doesn't mean that it is non-nil now.

On the other hand, what happens if a variable is captured by a block? Do we know that the variable is not mutated from another thread? Should we disable all these rules as soon as there is a block somewhere that captures one of the variables and might mutate them?

I just started thinking... For all these reasons, I don't see how this is going to be implemented in a way that is consistent and that doesn't introduce all kinds of strange edge cases. And any solution should be comprehensible by a Swift-beginner IMHO. On the other hand, if I ever forget a "!" somewhere, and the compiler compiles it anyways because it knows that it is safe, I will not complain :slight_smile:

-Michael

···

Am 29.04.2016 um 16:37 schrieb Tod Cunningham via swift-evolution <swift-evolution@swift.org>:

I'm new to the swift evolution community, but I wanted to toss an idea out there to get some feedback on it. So here it goes...

Currently, if you assign a non-nil value to an optional and then want to access that optional later, in the same context, you need to manually unwrap the value. This is usually done either by using "!" or by using something like "if let" or guard.

What would it be like if the compiler could auto unwrap, in cases where in knows the optional will have some value? This would make the code "clean" and still be safe.

This concept of Auto Unwrapping of Optionals is similar to Implicitly Unwrapped Optionals, but is only applied when the compiler knows it is safe to do so.

Take the following example:

class Test {
   var today: NSDate? = nil
   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as an optional
       let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // Requires ! or (if let) to unwrap
       // ... do stuff with timeInterval ...
   }
}

With the above example, the compiler could known that today has a value after it's set in the test method. So why couldn't the compiler auto unwrap it when accessed? This would mean manual unwrapping would be unnecessary:

class Test {
   var today: NSDate? = nil
   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as a value (not an optional)
       let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
       // ... do stuff with timeInterval ...
   }
}

If the value later gets set to an optional value, then it will no longer be auto unwrapable :

class Test {
   var today: NSDate? = nil

   func optionalDay() -> NSDate? {
       return NSDate()
   }

   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as a value (not an optional)
       let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
       let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed

       // If today is assigned an optional value, we can no longer auto unwrap it
       today = optionalDay()
       print("Today is \(today)") // Would be printed as an optional
       let timeInterval3: NSTimeInterval = today!.timeIntervalSinceNow // manual unwrapping would be required
   }
}

Note in the above example, explicit unwrapping would still be allow. The variable is still an optional. This allows for existing code to remain unchanged.

This change would encourage less use of forced unwrapping "!", generally require the developer to write less code, and would maintain code safety. On the down side, it is performing some compiler “magic”. It would be yet another thing to explain when trying to introduce people to swift and especially optionals.

What do you all think, would something like this be worth pursuing, what other pluses or minus would this introduce, has something like this already been discussed?

Thanks,
Tod Cunningham

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


(Rod Brown) #5

I agree that it's a pain to have to unwrap after this, but this proposal worries me somewhat.

I don't think we can safely guarantee that the value is non-null outside a very limited subset of cases, and we're breaking the simple, general and reasonable syntax of Swift for a very minor convenience win.

Additionally, this will play out like magic. Suddenly an optional starts dynamically changing its behaviour. I can't see where the compiler begins or ends the assertion of the value's non-null state, and so you have code that, with a little shifting around, begins to fail to even compile, based on something the user cannot see.

I think we have better mechanisms to handle this type of thing, like the if/guard let syntax, and implicitly unwrapped optionals.

···

On 30 Apr 2016, at 12:37 AM, Tod Cunningham via swift-evolution <swift-evolution@swift.org> wrote:

I'm new to the swift evolution community, but I wanted to toss an idea out there to get some feedback on it. So here it goes...

Currently, if you assign a non-nil value to an optional and then want to access that optional later, in the same context, you need to manually unwrap the value. This is usually done either by using "!" or by using something like "if let" or guard.

What would it be like if the compiler could auto unwrap, in cases where in knows the optional will have some value? This would make the code "clean" and still be safe.

This concept of Auto Unwrapping of Optionals is similar to Implicitly Unwrapped Optionals, but is only applied when the compiler knows it is safe to do so.

Take the following example:

class Test {
   var today: NSDate? = nil
   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as an optional
       let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // Requires ! or (if let) to unwrap
       // ... do stuff with timeInterval ...
   }
}

With the above example, the compiler could known that today has a value after it's set in the test method. So why couldn't the compiler auto unwrap it when accessed? This would mean manual unwrapping would be unnecessary:

class Test {
   var today: NSDate? = nil
   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as a value (not an optional)
       let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
       // ... do stuff with timeInterval ...
   }
}

If the value later gets set to an optional value, then it will no longer be auto unwrapable :

class Test {
   var today: NSDate? = nil

   func optionalDay() -> NSDate? {
       return NSDate()
   }

   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as a value (not an optional)
       let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
       let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed

       // If today is assigned an optional value, we can no longer auto unwrap it
       today = optionalDay()
       print("Today is \(today)") // Would be printed as an optional
       let timeInterval3: NSTimeInterval = today!.timeIntervalSinceNow // manual unwrapping would be required
   }
}

Note in the above example, explicit unwrapping would still be allow. The variable is still an optional. This allows for existing code to remain unchanged.

This change would encourage less use of forced unwrapping "!", generally require the developer to write less code, and would maintain code safety. On the down side, it is performing some compiler “magic”. It would be yet another thing to explain when trying to introduce people to swift and especially optionals.

What do you all think, would something like this be worth pursuing, what other pluses or minus would this introduce, has something like this already been discussed?

Thanks,
Tod Cunningham

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


(Alan Skipp) #6

I can’t say I’m keen on adding further compiler magic to Optionals, each special case adds further complication to the concept.

An alternative take on the example code posted would be: should the property be an Optional? If there is an Optional property which can later be replaced with a default value (today = today ?? NSDate()), perhaps the property should have been assigned the default non-optional value to begin with? Avoiding having to deal with the ceremony of Optionals all together.

I appreciate that the example code you posted was a succinct way to demonstrate the use case you had in mind and not meant as a `real world` case, but it does highlight that if a property has a valid default value, it probably shouldn’t be an Optional. The great thing about Optionals is that there’s the choice to avoid them as much as possible : )

···

On 29 Apr 2016, at 15:37, Tod Cunningham via swift-evolution <swift-evolution@swift.org> wrote:

This concept of Auto Unwrapping of Optionals is similar to Implicitly Unwrapped Optionals, but is only applied when the compiler knows it is safe to do so.

Take the following example:

class Test {
   var today: NSDate? = nil
   func test() {
       today = today ?? NSDate()
       print("Today is \(today)") // Would be printed as an optional
       let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // Requires ! or (if let) to unwrap
       // ... do stuff with timeInterval ...
   }
}


(Cristián Arenas Ulloa) #7

I think the programmer knows best in a case like that.
If I know it has a valid value, I wouldn't use an optional:

class Test {
    var today: NSDate? = nil
    func test() {
        let today = self.today ?? NSDate()
        self.today = today
        print("Today is \(today)") // Would not be printed as an optional
        let timeInterval: NSTimeInterval = today.timeIntervalSinceNow
    }
}

What do you think about that?

···

I'm new to the swift evolution community, but I wanted to toss an idea out there to get some feedback on it. So here it goes...

Currently, if you assign a non-nil value to an optional and then want to access that optional later, in the same context, you need to manually unwrap the value. This is usually done either by using "!" or by using something like "if let" or guard.

What would it be like if the compiler could auto unwrap, in cases where in knows the optional will have some value? This would make the code "clean" and still be safe.

This concept of Auto Unwrapping of Optionals is similar to Implicitly Unwrapped Optionals, but is only applied when the compiler knows it is safe to do so.

Take the following example:

class Test {
var today: NSDate? = nil
func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as an optional
let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // Requires ! or (if let) to unwrap
// ... do stuff with timeInterval ...
}
}

With the above example, the compiler could known that today has a value after it's set in the test method. So why couldn't the compiler auto unwrap it when accessed? This would mean manual unwrapping would be unnecessary:

class Test {
var today: NSDate? = nil
func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as a value (not an optional)
let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
// ... do stuff with timeInterval ...
}
}

If the value later gets set to an optional value, then it will no longer be auto unwrapable :

class Test {
var today: NSDate? = nil

func optionalDay() ->NSDate? {
return NSDate()
}

func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as a value (not an optional)
let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed

// If today is assigned an optional value, we can no longer auto unwrap it
today = optionalDay()
print("Today is \(today)") // Would be printed as an optional
let timeInterval3: NSTimeInterval = today!.timeIntervalSinceNow // manual unwrapping would be required
}
}

Note in the above example, explicit unwrapping would still be allow. The variable is still an optional. This allows for existing code to remain unchanged.

This change would encourage less use of forced unwrapping "!", generally require the developer to write less code, and would maintain code safety. On the down side, it is performing some compiler “magic”. It would be yet another thing to explain when trying to introduce people to swift and especially optionals.

What do you all think, would something like this be worth pursuing, what other pluses or minus would this introduce, has something like this already been discussed?

Thanks,
Tod Cunningham


(Tod Cunningham) #8

Adrian, excellent example of a challenging case. I would say that when calling any method that might mutate the value, the compiler would no longer be able to safely auto unwrap. That really limits the usefulness of this capability, at least for classes. For classes that would mean any call that would leave the current context would disable the auto unwrapping. For structs, it would be any mutating method would disable the auto unwrap.

I modified my example a bit to show how this would effect the ability to auto unwrap.

class Test {
  var today: NSDate? = nil

  func test() {
     today = today ?? NSDate()
     let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)

     // today can no longer be auto unwrapped as calling timeIntervalSinceNow has escaped
     // the enclosing context and could cause side effects with this instance.
}

It would be nice if the compiler could know that timeIntervalSinceNow had no dependencies or knowledge of class Test, but I doubt that would be practical.

However if Test was a struct the mutation information is readily available, so we know these calls would be safe:

struct Test {
    var today: NSDate? = nil

    mutating func test() {
        today = today ?? NSDate()
        let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // No ! required (auto unwrapped)
        let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed
        print("Today is \(today)") // Would be printed as a value (not an optional)

        // today can still be auto unwrapped as it won't be mutated by timeIntervalSinceNow or print
    }
}

···

From: <swift-evolution-bounces@swift.org<mailto:swift-evolution-bounces@swift.org>> on behalf of Adrian Zubarev via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>>
Reply-To: Adrian Zubarev <adrian.zubarev@devandartist.com<mailto:adrian.zubarev@devandartist.com>>
Date: Friday, April 29, 2016 at 3:21 PM
To: "swift-evolution@swift.org<mailto:swift-evolution@swift.org>" <swift-evolution@swift.org<mailto:swift-evolution@swift.org>>
Subject: Re: [swift-evolution] Auto Unwrapping Of Optionals

+1 But your example is too good to be true. :slight_smile:

What would happen to this code:

class A {

    var value: Type? = nil

    func reset() { self.value = nil }

    func test() {

        self.value = self.value ?? Type()

        self.reset()

        self.value.doSomething()

        // can the compiler be sure that our value wasn't reset somewhere from a different scope ?
     }
}

I'm curious what will happen here. Can someone clarify on that?

--
Adrian Zubarev

Am 29. April 2016 um 16:37:37, Tod Cunningham via swift-evolution (swift-evolution@swift.org<mailto:swift-evolution@swift.org>) schrieb:

I'm new to the swift evolution community, but I wanted to toss an idea out there to get some feedback on it. So here it goes...

Currently, if you assign a non-nil value to an optional and then want to access that optional later, in the same context, you need to manually unwrap the value. This is usually done either by using "!" or by using something like "if let" or guard.

What would it be like if the compiler could auto unwrap, in cases where in knows the optional will have some value? This would make the code "clean" and still be safe.

This concept of Auto Unwrapping of Optionals is similar to Implicitly Unwrapped Optionals, but is only applied when the compiler knows it is safe to do so.

Take the following example:

class Test {
var today: NSDate? = nil
func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as an optional
let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // Requires ! or (if let) to unwrap
// ... do stuff with timeInterval ...
}
}

With the above example, the compiler could known that today has a value after it's set in the test method. So why couldn't the compiler auto unwrap it when accessed? This would mean manual unwrapping would be unnecessary:

class Test {
var today: NSDate? = nil
func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as a value (not an optional)
let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
// ... do stuff with timeInterval ...
}
}

If the value later gets set to an optional value, then it will no longer be auto unwrapable :

class Test {
var today: NSDate? = nil

func optionalDay() -> NSDate? {
return NSDate()
}

func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as a value (not an optional)
let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed

// If today is assigned an optional value, we can no longer auto unwrap it
today = optionalDay()
print("Today is \(today)") // Would be printed as an optional
let timeInterval3: NSTimeInterval = today!.timeIntervalSinceNow // manual unwrapping would be required
}
}

Note in the above example, explicit unwrapping would still be allow. The variable is still an optional. This allows for existing code to remain unchanged.

This change would encourage less use of forced unwrapping "!", generally require the developer to write less code, and would maintain code safety. On the down side, it is performing some compiler “magic”. It would be yet another thing to explain when trying to introduce people to swift and especially optionals.

What do you all think, would something like this be worth pursuing, what other pluses or minus would this introduce, has something like this already been discussed?

Thanks,
Tod Cunningham

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


(James Campbell) #9

Wouldn't calling the NSDate constructor escape the context ?

···

Sent from Supmenow.com

On Fri, Apr 29, 2016 at 1:47 PM -0700, "Tod Cunningham via swift-evolution" <swift-evolution@swift.org> wrote:

Adrian, excellent example of a challenging case. I would say that when calling any method that might mutate the value, the compiler would no longer be able to safely auto unwrap. That really limits the usefulness of this capability, at least for classes. For classes that would mean any call that would leave the current context would disable the auto unwrapping. For structs, it would be any mutating method would disable the auto unwrap.

I modified my example a bit to show how this would effect the ability to auto unwrap.

class Test {
  var today: NSDate? = nil

  func test() {
     today = today ?? NSDate()
     let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)

     // today can no longer be auto unwrapped as calling timeIntervalSinceNow has escaped
     // the enclosing context and could cause side effects with this instance.
}

It would be nice if the compiler could know that timeIntervalSinceNow had no dependencies or knowledge of class Test, but I doubt that would be practical.

However if Test was a struct the mutation information is readily available, so we know these calls would be safe:

struct Test {
    var today: NSDate? = nil

    mutating func test() {
        today = today ?? NSDate()
        let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // No ! required (auto unwrapped)
        let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed
        print("Today is \(today)") // Would be printed as a value (not an optional)

        // today can still be auto unwrapped as it won't be mutated by timeIntervalSinceNow or print
    }
}

From: > on behalf of Adrian Zubarev via swift-evolution >
Reply-To: Adrian Zubarev >
Date: Friday, April 29, 2016 at 3:21 PM
To: "swift-evolution@swift.org" >
Subject: Re: [swift-evolution] Auto Unwrapping Of Optionals

+1 But your example is too good to be true. :slight_smile:

What would happen to this code:

class A {

    var value: Type? = nil

    func reset() { self.value = nil }

    func test() {

        self.value = self.value ?? Type()

        self.reset()

        self.value.doSomething()

        // can the compiler be sure that our value wasn't reset somewhere from a different scope ?
     }
}

I'm curious what will happen here. Can someone clarify on that?

--
Adrian Zubarev

Am 29. April 2016 um 16:37:37, Tod Cunningham via swift-evolution (swift-evolution@swift.org) schrieb:

I'm new to the swift evolution community, but I wanted to toss an idea out there to get some feedback on it. So here it goes...

Currently, if you assign a non-nil value to an optional and then want to access that optional later, in the same context, you need to manually unwrap the value. This is usually done either by using "!" or by using something like "if let" or guard.

What would it be like if the compiler could auto unwrap, in cases where in knows the optional will have some value? This would make the code "clean" and still be safe.

This concept of Auto Unwrapping of Optionals is similar to Implicitly Unwrapped Optionals, but is only applied when the compiler knows it is safe to do so.

Take the following example:

class Test {
var today: NSDate? = nil
func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as an optional
let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow // Requires ! or (if let) to unwrap
// ... do stuff with timeInterval ...
}
}

With the above example, the compiler could known that today has a value after it's set in the test method. So why couldn't the compiler auto unwrap it when accessed? This would mean manual unwrapping would be unnecessary:

class Test {
var today: NSDate? = nil
func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as a value (not an optional)
let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
// ... do stuff with timeInterval ...
}
}

If the value later gets set to an optional value, then it will no longer be auto unwrapable :

class Test {
var today: NSDate? = nil

func optionalDay() -> NSDate? {
return NSDate()
}

func test() {
today = today ?? NSDate()
print("Today is \(today)") // Would be printed as a value (not an optional)
let timeInterval: NSTimeInterval = today.timeIntervalSinceNow // No ! required (auto unwrapped)
let timeInterval2: NSTimeInterval = today!.timeIntervalSinceNow // Explicit unwrapping would still be allowed

// If today is assigned an optional value, we can no longer auto unwrap it
today = optionalDay()
print("Today is \(today)") // Would be printed as an optional
let timeInterval3: NSTimeInterval = today!.timeIntervalSinceNow // manual unwrapping would be required
}
}

Note in the above example, explicit unwrapping would still be allow. The variable is still an optional. This allows for existing code to remain unchanged.

This change would encourage less use of forced unwrapping "!", generally require the developer to write less code, and would maintain code safety. On the down side, it is performing some compiler “magic”. It would be yet another thing to explain when trying to introduce people to swift and especially optionals.

What do you all think, would something like this be worth pursuing, what other pluses or minus would this introduce, has something like this already been discussed?

Thanks,
Tod Cunningham

_______________________________________________
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


(Haravikk) #10

Actually, thinking about it a bit more I think that the main case where I would want something like this is actually, something more like the following:

  if var unwrappedFoo = self.foo {
    unwrappedFoo.mutate() // self.foo is unchanged, we have to do this:
    self.foo!.mutate() // original is changed, but unwrappedFoo is not
    // foo was changed once, not twice
  }

Of course classes follow different rules, but it’s a little counter-intuitive for structs, what if we could do something like this:

  if inout unwrappedFoo = self.foo {
    unwrappedFoo.mutate() // both are changed
    self.foo!.mutate() // no longer required this
    // foo was changed twice, and both forms are consistent
  }

i.e- inout in this case gets us a non-optional reference to the original value, not a copy (or potential copy). In the event that self.foo is changed somewhere else, unwrappedFoo would continue to reference to what it used to be and remain usable, though of course in the above example the call to self.foo! would fail in such a case, but it should no longer be required.

At least in my experience this is the most common case that I encounter, as I try to use if let or guard let (or var) for safety, but then this doesn’t help when manipulating a struct, so a third option could be useful in such cases.

···

On 29 Apr 2016, at 23:28, Rod Brown via swift-evolution <swift-evolution@swift.org> wrote:

I agree that it's a pain to have to unwrap after this, but this proposal worries me somewhat.

I don't think we can safely guarantee that the value is non-null outside a very limited subset of cases, and we're breaking the simple, general and reasonable syntax of Swift for a very minor convenience win.

Additionally, this will play out like magic. Suddenly an optional starts dynamically changing its behaviour. I can't see where the compiler begins or ends the assertion of the value's non-null state, and so you have code that, with a little shifting around, begins to fail to even compile, based on something the user cannot see.

I think we have better mechanisms to handle this type of thing, like the if/guard let syntax, and implicitly unwrapped optionals.


(Rod Brown) #11

I definitely agree there are difficulties with working with nullability and structs. Interesting perspective on how to deal with that. I'd be interested in what others think of your inout type idea.

I think this specific proposal asking for compiler magic to auto-unwrap invisibly and only in very limited cases, as this proposal suggests, ends up breaking a lot more than it fixes. I can only see circumstances of this working with variables in the current scope, as anything like a property could be updated by other methods, threads etc, and the compiler couldn't be certain of state.

I think a language feature like you describe would be a lot more helpful, but I'd love to hear others' views on that.

- Rod

···

On 30 Apr 2016, at 5:22 PM, Haravikk <swift-evolution@haravikk.me> wrote:

On 29 Apr 2016, at 23:28, Rod Brown via swift-evolution <swift-evolution@swift.org> wrote:

I agree that it's a pain to have to unwrap after this, but this proposal worries me somewhat.

I don't think we can safely guarantee that the value is non-null outside a very limited subset of cases, and we're breaking the simple, general and reasonable syntax of Swift for a very minor convenience win.

Additionally, this will play out like magic. Suddenly an optional starts dynamically changing its behaviour. I can't see where the compiler begins or ends the assertion of the value's non-null state, and so you have code that, with a little shifting around, begins to fail to even compile, based on something the user cannot see.

I think we have better mechanisms to handle this type of thing, like the if/guard let syntax, and implicitly unwrapped optionals.

Actually, thinking about it a bit more I think that the main case where I would want something like this is actually, something more like the following:

  if var unwrappedFoo = self.foo {
    unwrappedFoo.mutate() // self.foo is unchanged, we have to do this:
    self.foo!.mutate() // original is changed, but unwrappedFoo is not
    // foo was changed once, not twice
  }

Of course classes follow different rules, but it’s a little counter-intuitive for structs, what if we could do something like this:

  if inout unwrappedFoo = self.foo {
    unwrappedFoo.mutate() // both are changed
    self.foo!.mutate() // no longer required this
    // foo was changed twice, and both forms are consistent
  }

i.e- inout in this case gets us a non-optional reference to the original value, not a copy (or potential copy). In the event that self.foo is changed somewhere else, unwrappedFoo would continue to reference to what it used to be and remain usable, though of course in the above example the call to self.foo! would fail in such a case, but it should no longer be required.

At least in my experience this is the most common case that I encounter, as I try to use if let or guard let (or var) for safety, but then this doesn’t help when manipulating a struct, so a third option could be useful in such cases.


(David Sweeris) #12

This can be handled with the if-let syntax:
if let foo = foo as? SomeType { … }
although I think your idea is more readable.

- Dave Sweeris

···

On Apr 29, 2016, at 1:23 PM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

This is definitely something I’m hoping to see as well, alongside more intelligent handling of the is keyword, as currently Swift doesn’t handle the following either:

  if foo is SomeType { /* foo could be safely used as SomeType here, but currently is not */ }

Hopefully someone more familiar can weigh in, as it seems like something I expect to be on the way but perhaps has been delayed in case any further changes to the type system were required?


(David Sweeris) #13

Yeah, auto-unwrapping "wherever it might be possible" seems too magical to me. I wouldn’t object to the compiler auto-unwraping optionals within a well defined code block, though:
//foo is T?
if foo != nil {
//foo is T within this set of curly braces
}
But even that invokes a bit of compiler magic, in that for this one type of enum (`Optional`), the compiler knows that if it isn’t one case, it must be the other. I’d prefer a more general solution…

What if the “is” keyword could function as a kind of incomplete switch?
var foo: UnicodeDecodingResult
...
if foo is .Result {
    //since we know foo is a result, `foo` refers to foo's associated or raw value within this set of curly braces
}
This allows the language feature (and relevant compiler code paths) to be used with any enum, not just Optionals. The “optional unwrapping behavior" could then be written like this:
var bar = 4 as Int?
...
if bar is .Some {
    //bar is 4 within this set of curly braces
}

- Dave Sweeris

···

On Apr 30, 2016, at 7:18 AM, Rod Brown via swift-evolution <swift-evolution@swift.org> wrote:

I think this specific proposal asking for compiler magic to auto-unwrap invisibly and only in very limited cases, as this proposal suggests, ends up breaking a lot more than it fixes. I can only see circumstances of this working with variables in the current scope, as anything like a property could be updated by other methods, threads etc, and the compiler couldn't be certain of state.

I think a language feature like you describe would be a lot more helpful, but I'd love to hear others' views on that.

- Rod


(James Campbell) #14

That would be handy

···

Sent from Supmenow.com

On Sat, Apr 30, 2016 at 1:31 PM -0700, "David Sweeris via swift-evolution" <swift-evolution@swift.org> wrote:

On Apr 30, 2016, at 7:18 AM, Rod Brown via swift-evolution <swift-evolution@swift.org> wrote:
I think this specific proposal asking for compiler magic to auto-unwrap invisibly and only in very limited cases, as this proposal suggests, ends up breaking a lot more than it fixes. I can only see circumstances of this working with variables in the current scope, as anything like a property could be updated by other methods, threads etc, and the compiler couldn't be certain of state.
I think a language feature like you describe would be a lot more helpful, but I'd love to hear others' views on that.
- Rod
Yeah, auto-unwrapping "wherever it might be possible" seems too magical to me. I wouldn’t object to the compiler auto-unwraping optionals within a well defined code block, though://foo is T?if foo != nil {//foo is T within this set of curly braces}But even that invokes a bit of compiler magic, in that for this one type of enum (`Optional`), the compiler knows that if it isn’t one case, it must be the other. I’d prefer a more general solution…
What if the “is” keyword could function as a kind of incomplete switch?var foo: UnicodeDecodingResult...if foo is .Result { //since we know foo is a result, `foo` refers to foo's associated or raw value within this set of curly braces}This allows the language feature (and relevant compiler code paths) to be used with any enum, not just Optionals. The “optional unwrapping behavior" could then be written like this:var bar = 4 as Int?...if bar is .Some { //bar is 4 within this set of curly braces}
- Dave Sweeris


(Rod Brown) #15

Re-sent for Swift Evolution. Response at end.

···

On 1 May 2016, at 6:31 AM, David Sweeris <davesweeris@mac.com> wrote:

On Apr 30, 2016, at 7:18 AM, Rod Brown via swift-evolution <swift-evolution@swift.org> wrote:

I think this specific proposal asking for compiler magic to auto-unwrap invisibly and only in very limited cases, as this proposal suggests, ends up breaking a lot more than it fixes. I can only see circumstances of this working with variables in the current scope, as anything like a property could be updated by other methods, threads etc, and the compiler couldn't be certain of state.

I think a language feature like you describe would be a lot more helpful, but I'd love to hear others' views on that.

- Rod

Yeah, auto-unwrapping "wherever it might be possible" seems too magical to me. I wouldn’t object to the compiler auto-unwraping optionals within a well defined code block, though:
//foo is T?
if foo != nil {
//foo is T within this set of curly braces
}
But even that invokes a bit of compiler magic, in that for this one type of enum (`Optional`), the compiler knows that if it isn’t one case, it must be the other. I’d prefer a more general solution…

What if the “is” keyword could function as a kind of incomplete switch?
var foo: UnicodeDecodingResult
...
if foo is .Result {
    //since we know foo is a result, `foo` refers to foo's associated or raw value within this set of curly braces
}
This allows the language feature (and relevant compiler code paths) to be used with any enum, not just Optionals. The “optional unwrapping behavior" could then be written like this:
var bar = 4 as Int?
...
if bar is .Some {
    //bar is 4 within this set of curly braces
}

- Dave Sweeris

I think your idea makes a lot more sense in respect to ensuring we don't have as much magic.

That said, I still wonder about the implications for thread safety etc. While it isn't a focus of Swift 3, it's something to think about whether this promotes a paradigm that cannot be supported in a threaded environment, specifically accessing properties.

The if-let paradigm is a lot stronger for this set of actions. It gains a separate reference or copy to the internal value, and allows you to action it safely. Should the property change in the meantime, it isn't relevant, because you have you own reference/copy, and then you have the right to re-set the property as required.

This, however, would theoretically add in an invisible ! for you. This leaves you unable to handle the situation should the variable have been changed by another thread between your check and your subsequent action.

Unless I'm missing something, I worry about the behaviour of such a "feature" in a multithreaded environment. I think the previous "inout" idea actually held a lot more weight in this regard - at least then you can act on the copy, and have the change propagate to the main declaration, and overwrite any changes made on another thread.


(Tyler Cloutier) #16

The difference between the if-let syntax and the below syntax is that the type of foo never changes with the if-let syntax. Instead a new variable is created which shadows the original foo. The distinction is important because types don’t implicitly change in Swift. The implicit change is called "type narrowing”, that this has been discussed previously here: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/005475.html

I can definitely see the argument for no implicit type changes, but I can also see the argument that the syntax is much nicer.

Tyler

···

On Apr 30, 2016, at 10:11 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On Apr 29, 2016, at 1:23 PM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This is definitely something I’m hoping to see as well, alongside more intelligent handling of the is keyword, as currently Swift doesn’t handle the following either:

  if foo is SomeType { /* foo could be safely used as SomeType here, but currently is not */ }

Hopefully someone more familiar can weigh in, as it seems like something I expect to be on the way but perhaps has been delayed in case any further changes to the type system were required?

This can be handled with the if-let syntax:
if let foo = foo as? SomeType { … }
although I think your idea is more readable.

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


(David Sweeris) #17

I think it would have the same resiliency as if-let, since I was envisioning this to just be syntactic sugar for a switch statement. That is, this:
if foo is .Result { //`foo` refers to foo's associated or raw value within the following code block
    //code block
}
would get rewritten to this, for enums with associated values:
switch foo {
case .Result(let foo): //we get a local copy of `foo` (the associated value) for the following code block
    //code block
default: break
}
or this, for enums with raw values:
switch foo {
case .Result:
    let _foo = foo.rawValue //the compiler substitutes `_foo` for `foo`
    //code block
default: break
}

There’d have to be some more auto-generated code to copy assigned values back into the original `foo`, but I don’t think it’d be hard to do.

- Dave Sweeris

···

On Apr 30, 2016, at 5:42 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

Re-sent for Swift Evolution. Response at end.

On 1 May 2016, at 6:31 AM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:

On Apr 30, 2016, at 7:18 AM, Rod Brown via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I think this specific proposal asking for compiler magic to auto-unwrap invisibly and only in very limited cases, as this proposal suggests, ends up breaking a lot more than it fixes. I can only see circumstances of this working with variables in the current scope, as anything like a property could be updated by other methods, threads etc, and the compiler couldn't be certain of state.

I think a language feature like you describe would be a lot more helpful, but I'd love to hear others' views on that.

- Rod

Yeah, auto-unwrapping "wherever it might be possible" seems too magical to me. I wouldn’t object to the compiler auto-unwraping optionals within a well defined code block, though:
//foo is T?
if foo != nil {
//foo is T within this set of curly braces
}
But even that invokes a bit of compiler magic, in that for this one type of enum (`Optional`), the compiler knows that if it isn’t one case, it must be the other. I’d prefer a more general solution…

What if the “is” keyword could function as a kind of incomplete switch?
var foo: UnicodeDecodingResult
...
if foo is .Result {
    //since we know foo is a result, `foo` refers to foo's associated or raw value within this set of curly braces
}
This allows the language feature (and relevant compiler code paths) to be used with any enum, not just Optionals. The “optional unwrapping behavior" could then be written like this:
var bar = 4 as Int?
...
if bar is .Some {
    //bar is 4 within this set of curly braces
}

- Dave Sweeris

I think your idea makes a lot more sense in respect to ensuring we don't have as much magic.

That said, I still wonder about the implications for thread safety etc. While it isn't a focus of Swift 3, it's something to think about whether this promotes a paradigm that cannot be supported in a threaded environment, specifically accessing properties.

The if-let paradigm is a lot stronger for this set of actions. It gains a separate reference or copy to the internal value, and allows you to action it safely. Should the property change in the meantime, it isn't relevant, because you have you own reference/copy, and then you have the right to re-set the property as required.

This, however, would theoretically add in an invisible ! for you. This leaves you unable to handle the situation should the variable have been changed by another thread between your check and your subsequent action.

Unless I'm missing something, I worry about the behaviour of such a "feature" in a multithreaded environment. I think the previous "inout" idea actually held a lot more weight in this regard - at least then you can act on the copy, and have the change propagate to the main declaration, and overwrite any changes made on another thread.


(Rod Brown) #18

Ah yes, that makes sense. So how do you see the compiler dealing with the assignment/access problem on structs? If you assign to foo, the compiler assigns to both “_foo” and “foo”?

I wonder if we’re pushing down the road of convenience at the expense of truth. The if/guard let syntax is clear that you’re getting a separate reference or copy, but what you’re suggesting is hiding the reality from the user for what I see as relatively little convenience.

This is not to say I don’t see the problem, or the convenience… I just wonder if this might be going a little too far.

- Rod

···

On 1 May 2016, at 3:00 PM, David Sweeris <davesweeris@mac.com> wrote:

On Apr 30, 2016, at 5:42 PM, Rod Brown <rodney.brown6@icloud.com <mailto:rodney.brown6@icloud.com>> wrote:

Re-sent for Swift Evolution. Response at end.

On 1 May 2016, at 6:31 AM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:
I think your idea makes a lot more sense in respect to ensuring we don't have as much magic.

That said, I still wonder about the implications for thread safety etc. While it isn't a focus of Swift 3, it's something to think about whether this promotes a paradigm that cannot be supported in a threaded environment, specifically accessing properties.

The if-let paradigm is a lot stronger for this set of actions. It gains a separate reference or copy to the internal value, and allows you to action it safely. Should the property change in the meantime, it isn't relevant, because you have you own reference/copy, and then you have the right to re-set the property as required.

This, however, would theoretically add in an invisible ! for you. This leaves you unable to handle the situation should the variable have been changed by another thread between your check and your subsequent action.

Unless I'm missing something, I worry about the behaviour of such a "feature" in a multithreaded environment. I think the previous "inout" idea actually held a lot more weight in this regard - at least then you can act on the copy, and have the change propagate to the main declaration, and overwrite any changes made on another thread.

I think it would have the same resiliency as if-let, since I was envisioning this to just be syntactic sugar for a switch statement. That is, this:
if foo is .Result { //`foo` refers to foo's associated or raw value within the following code block
    //code block
}
would get rewritten to this, for enums with associated values:
switch foo {
case .Result(let foo): //we get a local copy of `foo` (the associated value) for the following code block
    //code block
default: break
}
or this, for enums with raw values:
switch foo {
case .Result:
    let _foo = foo.rawValue //the compiler substitutes `_foo` for `foo`
    //code block
default: break
}

There’d have to be some more auto-generated code to copy assigned values back into the original `foo`, but I don’t think it’d be hard to do.

- Dave Sweeris


(Tod Cunningham) #19

"I wonder if we’re pushing down the road of convenience at the expense of truth. The if/guard let syntax is clear that you’re getting a separate reference or copy, but what you’re suggesting is hiding the reality from the user for what I see as relatively little convenience."

It just might be me trying to avoid using !, and especially avoid implicit unwrapped options. While there is nothing wrong with the following code it makes me very uncomfortable from a defensive programming point of view:

        today = today ?? NSDate()
        let timeInterval = today!.timeIntervalSinceNow

Some developer coming along and changing the code could easily introduce a crash, such as by removing the default value. In the above example, such a change wouldn’t introduce a compiler warning/error and the bug might not reveal itself until a much later.

Also using if-let or guard also doesn’t seem right, in this case, as it should never fail:

   today = today ?? NSDate() // self.today changed!
   if let today = today {
      let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow
   } else {
      assertFailure()
   }

Same issue with guard:

   today = today ?? NSDate() // self.today changed!
   guard let today = today else {
      assertFailure()
      return // that should never happen
   }
   let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow

This introduces code that just gets in the way of the code’s meaning for cases that should never happen. Yuck, there has to be a better way!

- Tod

···

From: <swift-evolution-bounces@swift.org<mailto:swift-evolution-bounces@swift.org>> on behalf of Rod Brown via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>>
Reply-To: Rod Brown <rodney.brown6@icloud.com<mailto:rodney.brown6@icloud.com>>
Date: Sunday, May 1, 2016 at 1:25 AM
To: David Sweeris <davesweeris@mac.com<mailto:davesweeris@mac.com>>
Cc: Erica Sadun via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>>
Subject: Re: [swift-evolution] Auto Unwrapping Of Optionals

On 1 May 2016, at 3:00 PM, David Sweeris <davesweeris@mac.com<mailto:davesweeris@mac.com>> wrote:

On Apr 30, 2016, at 5:42 PM, Rod Brown <rodney.brown6@icloud.com<mailto:rodney.brown6@icloud.com>> wrote:

Re-sent for Swift Evolution. Response at end.

On 1 May 2016, at 6:31 AM, David Sweeris <davesweeris@mac.com<mailto:davesweeris@mac.com>> wrote:
I think your idea makes a lot more sense in respect to ensuring we don't have as much magic.

That said, I still wonder about the implications for thread safety etc. While it isn't a focus of Swift 3, it's something to think about whether this promotes a paradigm that cannot be supported in a threaded environment, specifically accessing properties.

The if-let paradigm is a lot stronger for this set of actions. It gains a separate reference or copy to the internal value, and allows you to action it safely. Should the property change in the meantime, it isn't relevant, because you have you own reference/copy, and then you have the right to re-set the property as required.

This, however, would theoretically add in an invisible ! for you. This leaves you unable to handle the situation should the variable have been changed by another thread between your check and your subsequent action.

Unless I'm missing something, I worry about the behaviour of such a "feature" in a multithreaded environment. I think the previous "inout" idea actually held a lot more weight in this regard - at least then you can act on the copy, and have the change propagate to the main declaration, and overwrite any changes made on another thread.

I think it would have the same resiliency as if-let, since I was envisioning this to just be syntactic sugar for a switch statement. That is, this:
if foo is .Result { //`foo` refers to foo's associated or raw value within the following code block
    //code block
}
would get rewritten to this, for enums with associated values:
switchfoo {
case .Result(let foo): //we get a local copy of `foo` (the associated value) for the following code block
    //code block
default: break
}
or this, for enums with raw values:
switchfoo {
case .Result:
    let _foo = foo.rawValue //the compiler substitutes `_foo` for `foo`
    //code block
default: break
}

There’d have to be some more auto-generated code to copy assigned values back into the original `foo`, but I don’t think it’d be hard to do.

- Dave Sweeris

Ah yes, that makes sense. So how do you see the compiler dealing with the assignment/access problem on structs? If you assign to foo, the compiler assigns to both “_foo” and “foo”?

I wonder if we’re pushing down the road of convenience at the expense of truth. The if/guard let syntax is clear that you’re getting a separate reference or copy, but what you’re suggesting is hiding the reality from the user for what I see as relatively little convenience.

This is not to say I don’t see the problem, or the convenience… I just wonder if this might be going a little too far.

- Rod


(David Waite) #20

It is a bad idea to have the compiler change the interpretation of a type without some hard and fast rules; the compiler’s interpretation of the optionality of your code will result in your code being legal or not.

In terms of solutions, I would prefer something similar to a guard statement that, rather than exiting, shadows a constant or variable with a non-optional equivalent type, e.g.

shadow var today = today ?? NSDate()
let timeInterval = today.timeIntervalSinceNow

-DW

···

On May 2, 2016, at 1:27 PM, Tod Cunningham via swift-evolution <swift-evolution@swift.org> wrote:

"I wonder if we’re pushing down the road of convenience at the expense of truth. The if/guard let syntax is clear that you’re getting a separate reference or copy, but what you’re suggesting is hiding the reality from the user for what I see as relatively little convenience."

It just might be me trying to avoid using !, and especially avoid implicit unwrapped options. While there is nothing wrong with the following code it makes me very uncomfortable from a defensive programming point of view:

       today = today ?? NSDate()
       let timeInterval = today!.timeIntervalSinceNow

Some developer coming along and changing the code could easily introduce a crash, such as by removing the default value. In the above example, such a change wouldn’t introduce a compiler warning/error and the bug might not reveal itself until a much later.

Also using if-let or guard also doesn’t seem right, in this case, as it should never fail:

  today = today ?? NSDate() // self.today changed!
  if let today = today {
     let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow
  } else {
     assertFailure()
  }

Same issue with guard:

  today = today ?? NSDate() // self.today changed!
  guard let today = today else {
     assertFailure()
     return // that should never happen
  }
  let timeInterval: NSTimeInterval = today!.timeIntervalSinceNow

This introduces code that just gets in the way of the code’s meaning for cases that should never happen. Yuck, there has to be a better way!

- Tod

From: <swift-evolution-bounces@swift.org<mailto:swift-evolution-bounces@swift.org>> on behalf of Rod Brown via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>>
Reply-To: Rod Brown <rodney.brown6@icloud.com<mailto:rodney.brown6@icloud.com>>
Date: Sunday, May 1, 2016 at 1:25 AM
To: David Sweeris <davesweeris@mac.com<mailto:davesweeris@mac.com>>
Cc: Erica Sadun via swift-evolution <swift-evolution@swift.org<mailto:swift-evolution@swift.org>>
Subject: Re: [swift-evolution] Auto Unwrapping Of Optionals

On 1 May 2016, at 3:00 PM, David Sweeris <davesweeris@mac.com<mailto:davesweeris@mac.com>> wrote:

On Apr 30, 2016, at 5:42 PM, Rod Brown <rodney.brown6@icloud.com<mailto:rodney.brown6@icloud.com>> wrote:

Re-sent for Swift Evolution. Response at end.

On 1 May 2016, at 6:31 AM, David Sweeris <davesweeris@mac.com<mailto:davesweeris@mac.com>> wrote:
I think your idea makes a lot more sense in respect to ensuring we don't have as much magic.

That said, I still wonder about the implications for thread safety etc. While it isn't a focus of Swift 3, it's something to think about whether this promotes a paradigm that cannot be supported in a threaded environment, specifically accessing properties.

The if-let paradigm is a lot stronger for this set of actions. It gains a separate reference or copy to the internal value, and allows you to action it safely. Should the property change in the meantime, it isn't relevant, because you have you own reference/copy, and then you have the right to re-set the property as required.

This, however, would theoretically add in an invisible ! for you. This leaves you unable to handle the situation should the variable have been changed by another thread between your check and your subsequent action.

Unless I'm missing something, I worry about the behaviour of such a "feature" in a multithreaded environment. I think the previous "inout" idea actually held a lot more weight in this regard - at least then you can act on the copy, and have the change propagate to the main declaration, and overwrite any changes made on another thread.

I think it would have the same resiliency as if-let, since I was envisioning this to just be syntactic sugar for a switch statement. That is, this:
if foo is .Result { //`foo` refers to foo's associated or raw value within the following code block
   //code block
}
would get rewritten to this, for enums with associated values:
switchfoo {
case .Result(let foo): //we get a local copy of `foo` (the associated value) for the following code block
   //code block
default: break
}
or this, for enums with raw values:
switchfoo {
case .Result:
   let _foo = foo.rawValue //the compiler substitutes `_foo` for `foo`
   //code block
default: break
}

There’d have to be some more auto-generated code to copy assigned values back into the original `foo`, but I don’t think it’d be hard to do.

- Dave Sweeris

Ah yes, that makes sense. So how do you see the compiler dealing with the assignment/access problem on structs? If you assign to foo, the compiler assigns to both “_foo” and “foo”?

I wonder if we’re pushing down the road of convenience at the expense of truth. The if/guard let syntax is clear that you’re getting a separate reference or copy, but what you’re suggesting is hiding the reality from the user for what I see as relatively little convenience.

This is not to say I don’t see the problem, or the convenience… I just wonder if this might be going a little too far.

- Rod

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