[swift-evolution] guard let x = x

Huon Wilson huon at apple.com
Fri Oct 28 19:22:57 CDT 2016


> On Oct 28, 2016, at 16:45, Erica Sadun <erica at ericasadun.com> wrote:
> 
> 
>> On Oct 28, 2016, at 5:00 PM, Huon Wilson <huon at apple.com <mailto:huon at apple.com>> wrote:
>> 
>> 
>>> On Oct 28, 2016, at 15:34, Erica Sadun via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>  <https://gist.github.com/erica/db9ce92b3d23cb20799460f603c0ae7c#detailed-design>Detailed Design
>>> 
>>> unwrap can be used with any one-value enumeration. The unwrapped value is bound to the same symbol as the associated type.
>>> 
>>> enum TypeName<T, U> { case anycase(T), anothercase(U) }
>>> 
>>> // First and second are type `TypeName`
>>> let first = TypeName.anyCase(value1)
>>> let second = TypeName. anothercase(value2)
>>> 
>>> guard unwrap first else { ... }
>>> // first is now shadowed as type T
>>> 
>>> guard unwrap second else { ... }
>>> // second is now shadowed as type U
>>>  <https://gist.github.com/erica/db9ce92b3d23cb20799460f603c0ae7c#impact-on-existing-code>
>> How does the compiler decide whether to succeed on anycase or succeed on anothercase respectively? In general, the compiler only statically knows that first & second are of type TypeName, not anything about which case (they could be passed in as function parameters, or returned by an opaque function e.g. `let x = OtherLibrary.returnsTypeName(); guard unwrap x else { … }`), and thus the variant to unwrap has to be chosen based only on that piece of information.
>> 
>> It seems to me that doing this either has to be restricted to enums with an “obvious” choice for unwrapping, like Optional, or rely on a sort of forward-looking type inference that Swift doesn’t currently use to deduce the unwrapped type based on how the value is used later (and I’m not sure that works in all cases, e.g. what if T == U for the TypeName example).
> 
> It succeeds on any one-item case and fails on any non-item case.
> 
> -- E
> 
> 

I think I obscured my point: `guard unwrap`ing two values of the same type should result in two values that also have identical types, because the type of the binding can only be determined from the type of the thing being unwrapped.

In particular, I was thinking that you’re proposing that the first example becomes:

guard case .anycase(let first) = first { ... }

(Which will fail if first is anothercase, of course.)

While the second example becomes:

guard case .anothercase(let second) = second { ... }

(Which will similarly fail if second is anycase.)

Somehow the compiler needs to have a rule that chooses to use anycase for the first one and anothercase for the second. If it doesn’t do this, the shadowed variables can’t be given types T and U. Unfortunately, I don’t see any cues that the compiler can use to make the decision about when to use each version, and so I don’t think such a rule can exist in Swift.


All that said, based on your clarification, it sounds like you might actually be actually envisioning a different desugaring. Consider the following:

enum Enum { case a(Int), b(String), c }
let x: Enum = returnsEnum();
guard unwrap x else { ... }

Maybe it could be equivalent to the following (using x_shadow instead of the new x for clarity):

let x_shadow: /* ??? */

switch x {
case let .a(t): x_shadow = t
case let .b(u): x_shadow = u
default: /* else clause */
}

However, this also doesn’t work: what type fills in ???. Any and things like it are the only choice, but erasing the type seems somewhat backwards for an unwrapping feature.


Huon

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161028/849f963e/attachment.html>


More information about the swift-evolution mailing list