<div dir="ltr"><div>Okay, I think I found an actual shortcoming that type narrowing might address, namely mutating a property in place if it is a subtype. Here is a small example setup:</div><div><br></div><div><div><font face="monospace, monospace">protocol A { var x: Int {get} }</font></div><div><font face="monospace, monospace">struct B: A { var x: Int }</font></div><div><font face="monospace, monospace">struct C { var a: A }</font></div><div><font face="monospace, monospace">var c = C(a: B(x: 4))</font></div><div><br></div><div>Note that “A” does not promise the “x” property will be mutable, while B does. I use “x: Int” as a minimal example, but any solution should also work for more complex scenarios.</div><div><br></div><div>Now suppose we wish to test whether “c.a” is of type B, and if so change its “x” value. We could, of course, make a local copy, mutate it, and reassign to “c.a”. But if those operations are expensive we would rather avoid doing so. And if B uses copy-on-write, we would want to remove the value from “c” entirely so that we hopefully have a unique reference. This is hard to get right.</div><div><br></div><div>We would prefer to write something like the following:</div><div><br></div><div><font face="monospace, monospace">(c.a as? B)?.x = 12</font></div></div><div><br></div><div>But that does not currently work, resulting in the error “Cannot assign to immutable expression of type 'Int'”.</div><div><br></div><div>Will the proposed type-narrowing feature provide a simple way to mutate “c.a” in place, contingent upon its being of type B?</div><div><br></div><div>How does it compare to an alternative such as inout return values, which could preserve mutability in the above?</div><div><br></div><div style="text-align:center">• • •</div><div><br></div>If we are going to have any sort of type narrowing, I would strongly prefer that it be explicit. For example we could use a keyword such as “rebind” to narrow the type of an existing symbol in a scope:<div><br></div><div><font face="monospace, monospace">if rebind c.a as B {</font></div><div><font face="monospace, monospace"> c.a.x = 12</font></div><div><font face="monospace, monospace">}</font></div><div><br></div><div>Furthermore, I think the proposal to treat enum cases as types is a major change to Swift’s type system, and probably introduces many unforeseen headaches. It also smells somewhat of a backdoor way for union types to sneak into the language.</div><div><br></div><div>Also, irrespective of everything else, you really need to make the “Motivation” section of your proposal a lot stronger. That section should stand on its own and make people understand exactly what problem you are trying to solve and why it is important.</div><div><br></div><div>Nevin</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Nov 3, 2016 at 5:43 PM, Haravikk <span dir="ltr"><<a href="mailto:swift-evolution@haravikk.me" target="_blank">swift-evolution@haravikk.me</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><br><div><span class=""><blockquote type="cite"><div>On 3 Nov 2016, at 19:23, Nevin Brackett-Rozinsky <<a href="mailto:nevin.brackettrozinsky@gmail.com" target="_blank">nevin.brackettrozinsky@gmail.<wbr>com</a>> wrote:</div><div><div dir="ltr"><div>For the motivating examples, there is already a much cleaner solution:</div><div><font face="monospace, monospace">(foo as? B)?.someMethodSpecificToB()</font><br></div></div></div></blockquote><div><br></div></span><div>Eh, kinda; you've taken an example showing branching and boiled it down into a single line; what if my intention was to call multiple B specific methods on foo? I think you've seen a simple example and tried to simplify it further, but I suppose I could put another method call in it to clarify.</div><div><br></div><div dir="ltr"><div><span class=""><blockquote type="cite"><div><div dir="ltr"><div>Aside from any implementation concerns, this proposal substantially increases the cognitive load on developers.</div></div></div></blockquote><div><br></div></span><div>I'm not so sure of this; the type is never going to be wider than what you originally set, so at the very least you can do whatever its original explicit type or inferred type would let you do, the main difference is that if you do something that makes no sense anymore then the type-checker can inform you of this. Otherwise if you know you've narrowed the type, then you can do things with that narrowed type and either the type-checker will allow it, or inform you that it's not possible, thus warning you that your conditional(s) etc. don't work as intended.</div><div><br></div><div>Really the burden is no different to the techniques that already exist; if you've tested a value with `is` then <b>you</b> know what the type is within that block of code, this is just ensuring that the type checker also knows it. Likewise with an optional, if you test that it's not nil then you know it's not, and so should the type-checker.</div><div><br></div><div>I should probably put more under motivation about why this feature works for the example cases given, and what the impact is on a larger scale; as the narrowing has the potential to pick up a bunch of little errors that standard type-checking alone may not, the trick is coming up with the example code to show it that isn't enormous, as I'm trying to avoid too much complexity in the Motivation section so I can build up the examples; maybe I'll add an "Advantages" section further down to detail more of what can be done *after* demonstrating the feature in stages, rather than trying to do it at the start.</div><span class=""><div><br></div><blockquote type="cite"><div dir="ltr"><div>the proposed “solution” has the same concurrency failure, but hides it even worse because the force-unwrap operator doesn’t even appear in the code:</div><div><br></div><div><div><font face="monospace, monospace">var foo:A? = A()</font></div><div><font face="monospace, monospace">if foo != nil {</font></div><div><font face="monospace, monospace"> foo.someMethod() // Bad news!</font></div><div><font face="monospace, monospace"> foo.someMutatingMethod() // Bad news!</font></div><div><font face="monospace, monospace">}</font></div></div></div></blockquote><div><br></div></span><div>Actually the issue <b>is</b> visible in the code; it's the conditional. If you can't trust `foo` not to change then testing its value and then acting upon the result without some form of local copy or locking is the part that explicitly isn't thread safe.</div><div><br></div><div>I'm not really sure of the need to focus on thread safety issues here as I'm not sure they're unique to this feature, or even to optionals; anything that is part of a shared class and thus potentially shared with other threads is unsafe, whether it's optional or not. While optionals might produce errors if force unwrapped and nil, they just as easily might not but end up with inconsistent values instead, so I'm not sure having the force unwrap operators actually makes you any safer; put another way, if you're relying on force unwrapping to catch thread safety issues then I'm not sure that's a good strategy, as it can only detect the issues at runtime, and only if the exact conditions necessary actually occur to trigger the failure.</div></div></div><div><br></div><div>I don't know what's planned for Swift's native concurrency support, but personally I'm hoping we might get a feature of classes that requires them (and/or their methods) to be marked as explicitly safe (except where it can be inferred), producing warnings otherwise, forcing developers to consider if their type/method has been properly reviewed for thread safety. Thread safety after all really is specific to classes, which are mostly discouraged in Swift anyway, so it makes sense to focus efforts towards making classes safer, and that you're handling your struct copies efficiently.</div><br><div dir="ltr"></div></div><div><div class="h5"><blockquote type="cite"><div><div dir="ltr"><div><blockquote type="cite">On Thu, Nov 3, 2016 at 1:04 PM, Haravikk via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:</blockquote></div></div><blockquote type="cite"><div dir="ltr"><div><blockquote type="cite"></blockquote></div></div><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word">To avoid hijacking the guard let x = x thread entirely I've decided to try to write up a proposal on type narrowing in Swift.<div>Please give your feedback on the functionality proposed, as well as the clarity of the proposal/examples themselves; I've tried to keep it straightforward, but I do tend towards being overly verbose, I've always tried to have the examples build upon one another to show how it all stacks up.<div><br></div><div><br></div><div><br></div><div><h1 style="box-sizing:border-box;margin-right:0px;margin-bottom:16px;margin-left:0px;line-height:1.25;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255);margin-top:0px!important">Type Narrowing</h1><ul style="box-sizing:border-box;padding-left:2em;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)"><li style="box-sizing:border-box">Proposal: <a href="https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none" target="_blank">SE-NNNN</a></li><li style="box-sizing:border-box;margin-top:0.25em">Author: <a href="https://github.com/haravikk" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none" target="_blank">Haravikk</a></li><li style="box-sizing:border-box;margin-top:0.25em">Status: <span style="box-sizing:border-box;font-weight:600">Awaiting review</span></li><li style="box-sizing:border-box;margin-top:0.25em">Review manager: TBD</li></ul><h2 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;line-height:1.25;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-introduction" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Introduction</h2><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">This proposal is to introduce type-narrowing to Swift, enabling the type-checker to automatically infer a narrower type from context such as conditionals.</p><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">Swift-evolution thread: <a href="http://news.gmane.org/gmane.comp.lang.swift.evolution" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none" target="_blank">Discussion thread topic for that proposal</a></p><h2 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;line-height:1.25;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-motivation" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Motivation</h2><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">Currently in Swift there are various pieces of boilerplate required in order to manually narrow types. The most obvious is in the case of polymorphism:</p><pre style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;margin-top:0px;margin-bottom:16px;line-height:1.45;word-wrap:normal;padding:16px;overflow:auto;background-color:rgb(247,247,247);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;color:rgb(51,51,51)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;padding:0px;margin:0px;background-color:transparent;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;word-break:normal;border:0px;display:inline;overflow:visible;line-height:inherit;word-wrap:normal">let foo:A = B() // B extends A
if foo is B {
(foo as B).someMethodSpecificToB()
}
</code></pre><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">But also in the case of unwrapping of optionals:</p><pre style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;margin-top:0px;margin-bottom:16px;line-height:1.45;word-wrap:normal;padding:16px;overflow:auto;background-color:rgb(247,247,247);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;color:rgb(51,51,51)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;padding:0px;margin:0px;background-color:transparent;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;word-break:normal;border:0px;display:inline;overflow:visible;line-height:inherit;word-wrap:normal">var foo:A? = A()
if var foo = foo { // foo is now unwrapped and shadowed
foo.someMethod()
foo!.someMutatingMethod() // Can't be done
}
</code></pre><h2 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;line-height:1.25;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-proposed-solution" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Proposed solution</h2><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">The proposed solution to the boiler-plate is to introduce type-narrowing, essentially a finer grained knowledge of type based upon context. Thus as any contextual clue indicating a more or less specific type are encountered, the type of the variable will reflect this from that point onwards.</p><h2 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;line-height:1.25;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-detailed-design" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Detailed design</h2><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">The concept of type-narrowing would essentially treat all variables as having not just a single type, but instead as having a stack of increasingly specific (narrow) types.</p><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">Whenever a contextual clue such as a conditional is encountered, the type checker will infer whether this narrows the type, and add the new narrow type to the stack from that point onwards. Whenever the type widens again narrower types are popped from the stack.</p><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">Here are the above examples re-written to take advantage of type-narrowing:</p><pre style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;margin-top:0px;margin-bottom:16px;line-height:1.45;word-wrap:normal;padding:16px;overflow:auto;background-color:rgb(247,247,247);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;color:rgb(51,51,51)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;padding:0px;margin:0px;background-color:transparent;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;word-break:normal;border:0px;display:inline;overflow:visible;line-height:inherit;word-wrap:normal">let foo:A = B() // B extends A
if foo is B { // B is added to foo's type stack
foo.someMethodSpecificToB()
}
// B is popped from foo's type stack
</code></pre><pre style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;margin-top:0px;margin-bottom:16px;line-height:1.45;word-wrap:normal;padding:16px;overflow:auto;background-color:rgb(247,247,247);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;color:rgb(51,51,51)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;padding:0px;margin:0px;background-color:transparent;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;word-break:normal;border:0px;display:inline;overflow:visible;line-height:inherit;word-wrap:normal">var foo:A? = A()
if foo != nil { // Optional<A>.some is added to foo's type stack
foo.someMethod()
foo.someMutatingMethod() // Can modify mutable original
}
// Optional<A>.some is popped from foo's type stack
</code></pre><h3 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;font-size:1.25em;line-height:1.25;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-enum-types" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Enum Types</h3><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">As seen in the simple optional example, to implement optional support each <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">case</code> in an <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">enum</code> is considered be a unique sub-type of the enum itself, thus allowing narrowing to <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">nil</code> (<code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">.none</code>) and non-<code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">nil</code> (<code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">.some</code>) types.</p><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">This behaviour actually enables some other useful behaviours, specifically, if a value is known to be either <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">nil</code> or non-<code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">nil</code> then the need to unwrap or force unwrap the value can be eliminated entirely, with the compiler able to produce errors if these are used incorrectly, for example:</p><pre style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;margin-top:0px;margin-bottom:16px;line-height:1.45;word-wrap:normal;padding:16px;overflow:auto;background-color:rgb(247,247,247);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;color:rgb(51,51,51)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;padding:0px;margin:0px;background-color:transparent;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;word-break:normal;border:0px;display:inline;overflow:visible;line-height:inherit;word-wrap:normal">var foo:A? = A()
foo.someMethod() // A is non-nil, no operators required!
foo = nil
foo!.someMethod() // Error: foo is always nil at this point
</code></pre><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">However, unwrapping of the value is only possible if the case contains either no value at all, or contains a single value able to satisfy the variable's original type requirements. In other words, the value stored in <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">Optional<A>.some</code> satisfies the type requirements of <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">var foo:A?</code>, thus it is implicitly unwrapped for use. For general enums this likely means no cases are implicitly unwrapped unless using a type of <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">Any</code>.</p><h3 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;font-size:1.25em;line-height:1.25;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-type-widening" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Type Widening</h3><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">In some cases a type may be narrowed, only to be used in a way that makes no sense for the narrowed type. In cases such as these the operation is tested against each type in the stack to determine whether the type must instead be widened. If a widened type is found it is selected (with re-narrowing where possible) otherwise an error is produced as normal.</p><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">For example:</p><pre style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;margin-top:0px;margin-bottom:16px;line-height:1.45;word-wrap:normal;padding:16px;overflow:auto;background-color:rgb(247,247,247);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;color:rgb(51,51,51)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;padding:0px;margin:0px;background-color:transparent;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;word-break:normal;border:0px;display:inline;overflow:visible;line-height:inherit;word-wrap:normal">let foo:A? = A()
if (foo != nil) { // Type of foo is Optional<A>.some
foo.someMethod()
foo = nil // Type of foo is widened to Optional<A>, then re-narrowed to Optional<A>.none
} // Type of foo is Optional<A>.none
foo.someMethod() // Error: foo is always nil at this point
</code></pre><h3 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;font-size:1.25em;line-height:1.25;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-multiple-conditions-and-branching" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Multiple Conditions and Branching</h3><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">When dealing with complex conditionals or branches, all paths must agree on a common type for narrowing to occur. For example:</p><pre style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;margin-top:0px;margin-bottom:16px;line-height:1.45;word-wrap:normal;padding:16px;overflow:auto;background-color:rgb(247,247,247);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;color:rgb(51,51,51)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;padding:0px;margin:0px;background-color:transparent;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;word-break:normal;border:0px;display:inline;overflow:visible;line-height:inherit;word-wrap:normal">let foo:A? = B() // B extends A
let bar:C = C() // C extends B
if (foo != nil) || (foo == bar) { // Optional<A>.some is added to foo's type stack
if foo is B { // Optional<B>.some is added to foo's type stack
foo.someMethodSpecificToB()
} // Optional<B>.some is popped from foo's type stack
foo = nil // Type of foo is re-narrowed as Optional<A>.none
} // Type of foo is Optional<A>.none in all branches
foo.someMethod() // Error: foo is always nil at this point
</code></pre><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">Here we can see that the extra condition <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">(foo == bar)</code> does not prevent type-narrowing, as the variable <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">bar</code> cannot be <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">nil</code> so both conditions require a type of <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">Optional<A>.some</code> as a minimum.</p><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">In this example <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">foo</code> is also <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">nil</code> at the end of both branches, thus its type can remain narrowed past this point.</p><h3 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;font-size:1.25em;line-height:1.25;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-context-triggers" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Context Triggers</h3><table style="box-sizing:border-box;border-spacing:0px;border-collapse:collapse;margin-top:0px;margin-bottom:16px;display:block;width:888px;overflow:auto;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)"><thead style="box-sizing:border-box"><tr style="box-sizing:border-box;border-top-width:1px;border-top-style:solid;border-top-color:rgb(204,204,204)"><th style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)">Trigger</th><th style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)">Impact</th></tr></thead><tbody style="box-sizing:border-box"><tr style="box-sizing:border-box;border-top-width:1px;border-top-style:solid;border-top-color:rgb(204,204,204)"><td style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">as</code></td><td style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)">Explicitly narrows a type with <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">as!</code> failing and <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">as?</code> narrowing to <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">Type?</code> instead when this is not possible.</td></tr><tr style="box-sizing:border-box;background-color:rgb(248,248,248);border-top-width:1px;border-top-style:solid;border-top-color:rgb(204,204,204)"><td style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">is</code></td><td style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)">Anywhere a type is tested will allow the type-checker to infer the new type if there was a match (and other conditions agree).</td></tr><tr style="box-sizing:border-box;border-top-width:1px;border-top-style:solid;border-top-color:rgb(204,204,204)"><td style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">case</code></td><td style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)">Any form of exhaustive test on an enum type allows it to be narrowed either to that case or the opposite, e.g- <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">foo != nil</code> eliminates <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">.none</code>, leaving only <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">.some</code> as the type, which can then be implicitly unwrapped (see Enum Types above).</td></tr><tr style="box-sizing:border-box;background-color:rgb(248,248,248);border-top-width:1px;border-top-style:solid;border-top-color:rgb(204,204,204)"><td style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)"><code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">=</code></td><td style="box-sizing:border-box;padding:6px 13px;border:1px solid rgb(221,221,221)">Assigning a value to a type will either narrow it if the new value is a sub-type, or will trigger widening to find a new common type, before attempting to re-narrow from there.</td></tr></tbody></table><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">There may be other triggers that should be considered.</p><h2 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;line-height:1.25;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-impact-on-existing-code" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Impact on existing code</h2><p style="box-sizing:border-box;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255)">Although this change is technically additive, it will impact any code in which there are currently errors that type-narrowing would have detected; for example, attempting to manipulate a predictably <code style="box-sizing:border-box;font-family:Consolas,'Liberation Mono',Menlo,Courier,monospace;font-size:14px;padding:0.2em 0px;margin:0px;background-color:rgba(0,0,0,0.0392157);border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px">nil</code> value.</p><h2 style="box-sizing:border-box;margin-top:24px;margin-bottom:16px;line-height:1.25;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';background-color:rgb(255,255,255)"><a id="m_6643613839773989483m_-7271769211814874654user-content-alternatives-considered" class="m_6643613839773989483m_-7271769211814874654anchor" href="https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered" style="box-sizing:border-box;background-color:transparent;color:rgb(64,120,192);text-decoration:none;float:left;padding-right:4px;line-height:1" target="_blank"><u></u><u></u><u></u><u></u></a>Alternatives considered</h2><div style="box-sizing:border-box;margin-top:0px;color:rgb(51,51,51);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:16px;background-color:rgb(255,255,255);margin-bottom:0px!important">One of the main advantages of type-narrowing is that it functions as an alternative to other features. This includes alternative syntax for shadowing/unwrapping of optionals, in which case type-narrowing allows an optional to be implicitly unwrapped simply by testing it, and without the need to introduce any new syntax.</div></div></div></div><br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailma<wbr>n/listinfo/swift-evolution</a><br>
<br></blockquote></div><br></div>
</blockquote></div><br></blockquote></div></div></div></blockquote></div><br></div>