# \[Proposal\] Type Narrowing

**URL:** <https://forums.swift.org/t/proposal-type-narrowing/4433>\
**Category:** Discussion\
**Created:** [November 3, 2016, 5:04pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433 "2016-11-03T17:04:38Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Haravikk3](https://avatars.discourse-cdn.com/v4/letter/h/df705f/32.png) [@Haravikk3](https://forums.swift.org/u/Haravikk3)\
**Post date:** [November 3, 2016, 5:04pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/1 "2016-11-03T17:04:38Z")

</div>

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.  
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.

Type Narrowing

Proposal: SE-NNNN \<[https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt](https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt);  
Author: Haravikk \<[https://github.com/haravikk&gt](https://github.com/haravikk&gt);  
Status: Awaiting review  
Review manager: TBD  
\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt;Introduction](https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt;Introduction)

This proposal is to introduce type-narrowing to Swift, enabling the type-checker to automatically infer a narrower type from context such as conditionals.

Swift-evolution thread: Discussion thread topic for that proposal \<[http://news.gmane.org/gmane.comp.lang.swift.evolution&gt](http://news.gmane.org/gmane.comp.lang.swift.evolution&gt);  
\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt;Motivation](https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt;Motivation)

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:

let foo:A = B() // B extends A  
if foo is B {  
&nbsp;&nbsp;&nbsp;&nbsp;(foo as B).someMethodSpecificToB()  
}  
But also in the case of unwrapping of optionals:

var foo:A? = A()  
if var foo = foo { // foo is now unwrapped and shadowed  
&nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
&nbsp;&nbsp;&nbsp;&nbsp;foo!.someMutatingMethod() // Can't be done  
}  
\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed) solution

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.

\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed) design

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.

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.

Here are the above examples re-written to take advantage of type-narrowing:

let foo:A = B() // B extends A  
if foo is B { // B is added to foo's type stack  
&nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
}  
// B is popped from foo's type stack  
var foo:A? = A()  
if foo != nil { // Optional\<A\>.some is added to foo's type stack  
&nbsp;&nbsp;&nbsp;foo.someMethod()  
&nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Can modify mutable original  
}  
// Optional\<A\>.some is popped from foo's type stack  
\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum](https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum) Types

As seen in the simple optional example, to implement optional support each case in an enum is considered be a unique sub-type of the enum itself, thus allowing narrowing to nil (.none) and non-nil (.some) types.

This behaviour actually enables some other useful behaviours, specifically, if a value is known to be either nil or non-nil 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:

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  
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 Optional\<A\>.some satisfies the type requirements of var foo:A?, thus it is implicitly unwrapped for use. For general enums this likely means no cases are implicitly unwrapped unless using a type of Any.

\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type](https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type) Widening

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.

For example:

let foo:A? = A()  
if (foo != nil) { // Type of foo is Optional\<A\>.some  
&nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
&nbsp;&nbsp;&nbsp;&nbsp;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  
\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple](https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple) Conditions and Branching

When dealing with complex conditionals or branches, all paths must agree on a common type for narrowing to occur. For example:

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  
&nbsp;&nbsp;&nbsp;&nbsp;if foo is B { // Optional\<B\>.some is added to foo's type stack  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
&nbsp;&nbsp;&nbsp;&nbsp;} // Optional\<B\>.some is popped from foo's type stack  
&nbsp;&nbsp;&nbsp;&nbsp;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  
Here we can see that the extra condition (foo == bar) does not prevent type-narrowing, as the variable bar cannot be nil so both conditions require a type of Optional\<A\>.some as a minimum.

In this example foo is also nil at the end of both branches, thus its type can remain narrowed past this point.

\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context](https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context) Triggers

Trigger Impact  
as Explicitly narrows a type with as! failing and as? narrowing to Type? instead when this is not possible.  
is Anywhere a type is tested will allow the type-checker to infer the new type if there was a match (and other conditions agree).  
case Any form of exhaustive test on an enum type allows it to be narrowed either to that case or the opposite, e.g- foo != nil eliminates .none, leaving only .some as the type, which can then be implicitly unwrapped (see Enum Types above).  
= 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.  
There may be other triggers that should be considered.

\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact](https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact) on existing code

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 nil value.

\<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives](https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives) considered

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 class="post-metadata">

**Author:** ![Callionica\_Swift](https://avatars.discourse-cdn.com/v4/letter/c/da6949/32.png) [@Callionica\_Swift](https://forums.swift.org/u/Callionica_Swift)\
**Post date:** [November 3, 2016, 5:47pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/2 "2016-11-03T17:47:07Z")

</div>

Great. I'd like to see something like this. Couple of comments:

You don't explicitly address overload selection. Do you intend it to  
be affected?

Impact on existing code section could be more descriptive.

> **···**
>
> On Thursday, November 3, 2016, Haravikk via swift-evolution \< swift-evolution@swift.org\> wrote:
> 
> > 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.  
> > 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.
> > 
> > Type Narrowing
> > 
> > &nbsp;&nbsp;&nbsp;- Proposal: SE-NNNN  
> > &nbsp;&nbsp;&nbsp;\<[https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt](https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt);  
> > &nbsp;&nbsp;&nbsp;- Author: Haravikk \<[https://github.com/haravikk&gt](https://github.com/haravikk&gt);  
> > &nbsp;&nbsp;&nbsp;- Status: Awaiting review  
> > &nbsp;&nbsp;&nbsp;- Review manager: TBD
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt](https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt);  
> > Introduction
> > 
> > This proposal is to introduce type-narrowing to Swift, enabling the  
> > type-checker to automatically infer a narrower type from context such as  
> > conditionals.
> > 
> > Swift-evolution thread: Discussion thread topic for that proposal  
> > \<[http://news.gmane.org/gmane.comp.lang.swift.evolution&gt](http://news.gmane.org/gmane.comp.lang.swift.evolution&gt);
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt](https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt);  
> > Motivation
> > 
> > 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:
> > 
> > let foo:A = B() // B extends A  
> > if foo is B {  
> > &nbsp;&nbsp;&nbsp;&nbsp;(foo as B).someMethodSpecificToB()  
> > }
> > 
> > But also in the case of unwrapping of optionals:
> > 
> > var foo:A? = A()  
> > if var foo = foo { // foo is now unwrapped and shadowed  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo!.someMutatingMethod() // Can't be done  
> > }
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed)  
> > solution
> > 
> > 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.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed)  
> > design
> > 
> > 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.
> > 
> > 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.
> > 
> > Here are the above examples re-written to take advantage of type-narrowing:
> > 
> > let foo:A = B() // B extends A  
> > if foo is B { // B is added to foo's type stack  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > }  
> > // B is popped from foo's type stack
> > 
> > var foo:A? = A()  
> > if foo != nil { // Optional\<A\>.some is added to foo's type stack  
> > &nbsp;&nbsp;&nbsp;foo.someMethod()  
> > &nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Can modify mutable original  
> > }  
> > // Optional\<A\>.some is popped from foo's type stack
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum](https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum)  
> > Types
> > 
> > As seen in the simple optional example, to implement optional support each  
> > case in an enum is considered be a unique sub-type of the enum itself,  
> > thus allowing narrowing to nil (.none) and non-nil (.some) types.
> > 
> > This behaviour actually enables some other useful behaviours,  
> > specifically, if a value is known to be either nil or non-nil 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:
> > 
> > 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
> > 
> > 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  
> > Optional\<A\>.some satisfies the type requirements of var foo:A?, thus it  
> > is implicitly unwrapped for use. For general enums this likely means no  
> > cases are implicitly unwrapped unless using a type of Any.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type](https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type)  
> > Widening
> > 
> > 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.
> > 
> > For example:
> > 
> > let foo:A? = A()  
> > if (foo != nil) { // Type of foo is Optional\<A\>.some  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > &nbsp;&nbsp;&nbsp;&nbsp;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
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple](https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple)  
> > Conditions and Branching
> > 
> > When dealing with complex conditionals or branches, all paths must agree  
> > on a common type for narrowing to occur. For example:
> > 
> > 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  
> > &nbsp;&nbsp;&nbsp;&nbsp;if foo is B { // Optional\<B\>.some is added to foo's type stack  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > &nbsp;&nbsp;&nbsp;&nbsp;} // Optional\<B\>.some is popped from foo's type stack  
> > &nbsp;&nbsp;&nbsp;&nbsp;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
> > 
> > Here we can see that the extra condition (foo == bar) does not prevent  
> > type-narrowing, as the variable bar cannot be nil so both conditions  
> > require a type of Optional\<A\>.some as a minimum.
> > 
> > In this example foo is also nil at the end of both branches, thus its  
> > type can remain narrowed past this point.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context](https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context)  
> > Triggers  
> > TriggerImpact  
> > as Explicitly narrows a type with as! failing and as? narrowing to Type? instead  
> > when this is not possible.  
> > is Anywhere a type is tested will allow the type-checker to infer the new  
> > type if there was a match (and other conditions agree).  
> > case Any form of exhaustive test on an enum type allows it to be narrowed  
> > either to that case or the opposite, e.g- foo != nil eliminates .none,  
> > leaving only .some as the type, which can then be implicitly unwrapped  
> > (see Enum Types above).  
> > = 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.
> > 
> > There may be other triggers that should be considered.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact](https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact)  
> > on existing code
> > 
> > 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 nil value.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives](https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives)  
> > considered  
> > 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 class="post-metadata">

**Author:** ![Nevin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nevin/32/617_2.png) [@Nevin](https://forums.swift.org/u/Nevin)\
**Post date:** [November 3, 2016, 7:23pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/3 "2016-11-03T19:23:16Z")

</div>

This looks like a lot of complexity for very little gain.

Aside from any implementation concerns, this proposal substantially  
increases the cognitive load on developers. To figure out what a piece of  
code means, someone reading it will have to mentally keep track of a “type  
stack” for every variable. That is the opposite of “clarity at the point of  
use”.

For the motivating examples, there is already a much cleaner solution:

(foo as? B)?.someMethodSpecificToB()

Even in the case where foo gets passed as an argument to a function that  
takes a B, so optional chaining is not available, we are still okay. If foo  
is an instance of a struct, then passing it to a function won’t change its  
value so we can write:

if let newFoo = foo as? B {  
&nbsp;&nbsp;&nbsp;&nbsp;funcThatTakesB(newFoo)  
}

And if foo is an instance of a class, then the same thing \*still\* works,  
because any changes the function makes to newFoo are also observed in foo  
since they reference the same object.

• • •

The proposal’s other example is actually a great illustration of why  
shadowing is undesirable. After all, simply binding to a different name  
solves the so-called problem, \*and\* lets us bind to a constant:

var foo: A? = A()  
if let newFoo = foo {  
&nbsp;&nbsp;&nbsp;&nbsp;newFoo.someMethod()  
&nbsp;&nbsp;&nbsp;&nbsp;foo!.someMutatingMethod() // Works now!  
}

Full disclosure: both this and the original example have a \*deeper\*  
problem, which is that they are not thread-safe. If foo is modified between  
the conditional binding and the force-unwrap, the force-unwrap could fail.  
Sure, that can’t happen when foo is a local variable like this, but if it  
were a property of a class then it could.

Moreover, 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:

var foo:A? = A()  
if foo != nil {  
&nbsp;&nbsp;&nbsp;foo.someMethod() // Bad news!  
&nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Bad news!  
}

In summary, I do not see sufficient motivation for this proposal. The  
motivation which I do see appears superfluous. The problem being described  
already has an elegant solution in Swift. And the proposed changes  
introduce an exceptionally taxing cognitive burden on developers just to  
figure out what the type of a variable is on any given line of code.

Plus, in the face of concurrency, such implicit type-narrowing has the  
potential to hide serious issues.

Nevin

> **···**
>
> On Thu, Nov 3, 2016 at 1:04 PM, Haravikk via swift-evolution \< swift-evolution@swift.org\> wrote:
> 
> > 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.  
> > 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.
> > 
> > Type Narrowing
> > 
> > &nbsp;&nbsp;&nbsp;- Proposal: SE-NNNN  
> > &nbsp;&nbsp;&nbsp;\<[https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt](https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt);  
> > &nbsp;&nbsp;&nbsp;- Author: Haravikk \<[https://github.com/haravikk&gt](https://github.com/haravikk&gt);  
> > &nbsp;&nbsp;&nbsp;- Status: Awaiting review  
> > &nbsp;&nbsp;&nbsp;- Review manager: TBD
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt](https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt);  
> > Introduction
> > 
> > This proposal is to introduce type-narrowing to Swift, enabling the  
> > type-checker to automatically infer a narrower type from context such as  
> > conditionals.
> > 
> > Swift-evolution thread: Discussion thread topic for that proposal  
> > \<[http://news.gmane.org/gmane.comp.lang.swift.evolution&gt](http://news.gmane.org/gmane.comp.lang.swift.evolution&gt);
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt](https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt);  
> > Motivation
> > 
> > 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:
> > 
> > let foo:A = B() // B extends A  
> > if foo is B {  
> > &nbsp;&nbsp;&nbsp;&nbsp;(foo as B).someMethodSpecificToB()  
> > }
> > 
> > But also in the case of unwrapping of optionals:
> > 
> > var foo:A? = A()  
> > if var foo = foo { // foo is now unwrapped and shadowed  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo!.someMutatingMethod() // Can't be done  
> > }
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed)  
> > solution
> > 
> > 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.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed)  
> > design
> > 
> > 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.
> > 
> > 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.
> > 
> > Here are the above examples re-written to take advantage of type-narrowing:
> > 
> > let foo:A = B() // B extends A  
> > if foo is B { // B is added to foo's type stack  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > }  
> > // B is popped from foo's type stack
> > 
> > var foo:A? = A()  
> > if foo != nil { // Optional\<A\>.some is added to foo's type stack  
> > &nbsp;&nbsp;&nbsp;foo.someMethod()  
> > &nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Can modify mutable original  
> > }  
> > // Optional\<A\>.some is popped from foo's type stack
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum](https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum)  
> > Types
> > 
> > As seen in the simple optional example, to implement optional support each  
> > case in an enum is considered be a unique sub-type of the enum itself,  
> > thus allowing narrowing to nil (.none) and non-nil (.some) types.
> > 
> > This behaviour actually enables some other useful behaviours,  
> > specifically, if a value is known to be either nil or non-nil 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:
> > 
> > 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
> > 
> > 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  
> > Optional\<A\>.some satisfies the type requirements of var foo:A?, thus it  
> > is implicitly unwrapped for use. For general enums this likely means no  
> > cases are implicitly unwrapped unless using a type of Any.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type](https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type)  
> > Widening
> > 
> > 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.
> > 
> > For example:
> > 
> > let foo:A? = A()  
> > if (foo != nil) { // Type of foo is Optional\<A\>.some  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > &nbsp;&nbsp;&nbsp;&nbsp;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
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple](https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple)  
> > Conditions and Branching
> > 
> > When dealing with complex conditionals or branches, all paths must agree  
> > on a common type for narrowing to occur. For example:
> > 
> > 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  
> > &nbsp;&nbsp;&nbsp;&nbsp;if foo is B { // Optional\<B\>.some is added to foo's type stack  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > &nbsp;&nbsp;&nbsp;&nbsp;} // Optional\<B\>.some is popped from foo's type stack  
> > &nbsp;&nbsp;&nbsp;&nbsp;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
> > 
> > Here we can see that the extra condition (foo == bar) does not prevent  
> > type-narrowing, as the variable bar cannot be nil so both conditions  
> > require a type of Optional\<A\>.some as a minimum.
> > 
> > In this example foo is also nil at the end of both branches, thus its  
> > type can remain narrowed past this point.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context](https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context)  
> > Triggers  
> > TriggerImpact  
> > as Explicitly narrows a type with as! failing and as? narrowing to Type? instead  
> > when this is not possible.  
> > is Anywhere a type is tested will allow the type-checker to infer the new  
> > type if there was a match (and other conditions agree).  
> > case Any form of exhaustive test on an enum type allows it to be narrowed  
> > either to that case or the opposite, e.g- foo != nil eliminates .none,  
> > leaving only .some as the type, which can then be implicitly unwrapped  
> > (see Enum Types above).  
> > = 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.
> > 
> > There may be other triggers that should be considered.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact](https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact)  
> > on existing code
> > 
> > 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 nil value.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives](https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives)  
> > considered  
> > 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.
> > 
> > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > swift-evolution mailing list  
> > swift-evolution@swift.org  
> > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)

---

<div class="post-metadata">

**Author:** ![xwu](https://avatars.discourse-cdn.com/v4/letter/x/b487fb/32.png) [@xwu](https://forums.swift.org/u/xwu)\
**Post date:** [November 3, 2016, 7:29pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/4 "2016-11-03T19:29:46Z")

</div>

This proposal seems well-reasoned, but like Nevin I worry about the  
interactions between type narrowing, reference types or capture of mutable  
values, and concurrency or async. How will these things play together?

> **···**
>
> On Thu, Nov 3, 2016 at 14:23 Nevin Brackett-Rozinsky via swift-evolution \< swift-evolution@swift.org\> wrote:
> 
> > This looks like a lot of complexity for very little gain.
> > 
> > Aside from any implementation concerns, this proposal substantially  
> > increases the cognitive load on developers. To figure out what a piece of  
> > code means, someone reading it will have to mentally keep track of a “type  
> > stack” for every variable. That is the opposite of “clarity at the point of  
> > use”.
> > 
> > For the motivating examples, there is already a much cleaner solution:
> > 
> > (foo as? B)?.someMethodSpecificToB()
> > 
> > Even in the case where foo gets passed as an argument to a function that  
> > takes a B, so optional chaining is not available, we are still okay. If foo  
> > is an instance of a struct, then passing it to a function won’t change its  
> > value so we can write:
> > 
> > if let newFoo = foo as? B {  
> > &nbsp;&nbsp;&nbsp;&nbsp;funcThatTakesB(newFoo)  
> > }
> > 
> > And if foo is an instance of a class, then the same thing \*still\* works,  
> > because any changes the function makes to newFoo are also observed in foo  
> > since they reference the same object.
> > 
> > • • •
> > 
> > The proposal’s other example is actually a great illustration of why  
> > shadowing is undesirable. After all, simply binding to a different name  
> > solves the so-called problem, \*and\* lets us bind to a constant:
> > 
> > var foo: A? = A()  
> > if let newFoo = foo {  
> > &nbsp;&nbsp;&nbsp;&nbsp;newFoo.someMethod()  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo!.someMutatingMethod() // Works now!  
> > }
> > 
> > Full disclosure: both this and the original example have a \*deeper\*  
> > problem, which is that they are not thread-safe. If foo is modified between  
> > the conditional binding and the force-unwrap, the force-unwrap could fail.  
> > Sure, that can’t happen when foo is a local variable like this, but if it  
> > were a property of a class then it could.
> > 
> > Moreover, 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:
> > 
> > var foo:A? = A()  
> > if foo != nil {  
> > &nbsp;&nbsp;&nbsp;foo.someMethod() // Bad news!  
> > &nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Bad news!  
> > }
> > 
> > In summary, I do not see sufficient motivation for this proposal. The  
> > motivation which I do see appears superfluous. The problem being described  
> > already has an elegant solution in Swift. And the proposed changes  
> > introduce an exceptionally taxing cognitive burden on developers just to  
> > figure out what the type of a variable is on any given line of code.
> > 
> > Plus, in the face of concurrency, such implicit type-narrowing has the  
> > potential to hide serious issues.
> > 
> > Nevin
> > 
> > On Thu, Nov 3, 2016 at 1:04 PM, Haravikk via swift-evolution \< \> swift-evolution@swift.org\> wrote:
> > 
> > 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.  
> > 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.
> > 
> > Type Narrowing
> > 
> > &nbsp;&nbsp;&nbsp;- Proposal: SE-NNNN  
> > &nbsp;&nbsp;&nbsp;\<[https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt](https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt);  
> > &nbsp;&nbsp;&nbsp;- Author: Haravikk \<[https://github.com/haravikk&gt](https://github.com/haravikk&gt);  
> > &nbsp;&nbsp;&nbsp;- Status: Awaiting review  
> > &nbsp;&nbsp;&nbsp;- Review manager: TBD
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt](https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt);  
> > Introduction
> > 
> > This proposal is to introduce type-narrowing to Swift, enabling the  
> > type-checker to automatically infer a narrower type from context such as  
> > conditionals.
> > 
> > Swift-evolution thread: Discussion thread topic for that proposal  
> > \<[http://news.gmane.org/gmane.comp.lang.swift.evolution&gt](http://news.gmane.org/gmane.comp.lang.swift.evolution&gt);
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt](https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt);  
> > Motivation
> > 
> > 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:
> > 
> > let foo:A = B() // B extends A  
> > if foo is B {  
> > &nbsp;&nbsp;&nbsp;&nbsp;(foo as B).someMethodSpecificToB()  
> > }
> > 
> > But also in the case of unwrapping of optionals:
> > 
> > var foo:A? = A()  
> > if var foo = foo { // foo is now unwrapped and shadowed  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo!.someMutatingMethod() // Can't be done  
> > }
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed)  
> > solution
> > 
> > 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.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed)  
> > design
> > 
> > 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.
> > 
> > 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.
> > 
> > Here are the above examples re-written to take advantage of type-narrowing:
> > 
> > let foo:A = B() // B extends A  
> > if foo is B { // B is added to foo's type stack  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > }  
> > // B is popped from foo's type stack
> > 
> > var foo:A? = A()  
> > if foo != nil { // Optional\<A\>.some is added to foo's type stack  
> > &nbsp;&nbsp;&nbsp;foo.someMethod()  
> > &nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Can modify mutable original  
> > }  
> > // Optional\<A\>.some is popped from foo's type stack
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum](https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum)  
> > Types
> > 
> > As seen in the simple optional example, to implement optional support each  
> > case in an enum is considered be a unique sub-type of the enum itself,  
> > thus allowing narrowing to nil (.none) and non-nil (.some) types.
> > 
> > This behaviour actually enables some other useful behaviours,  
> > specifically, if a value is known to be either nil or non-nil 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:
> > 
> > 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
> > 
> > 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  
> > Optional\<A\>.some satisfies the type requirements of var foo:A?, thus it  
> > is implicitly unwrapped for use. For general enums this likely means no  
> > cases are implicitly unwrapped unless using a type of Any.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type](https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type)  
> > Widening
> > 
> > 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.
> > 
> > For example:
> > 
> > let foo:A? = A()  
> > if (foo != nil) { // Type of foo is Optional\<A\>.some  
> > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > &nbsp;&nbsp;&nbsp;&nbsp;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
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple](https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple)  
> > Conditions and Branching
> > 
> > When dealing with complex conditionals or branches, all paths must agree  
> > on a common type for narrowing to occur. For example:
> > 
> > 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  
> > &nbsp;&nbsp;&nbsp;&nbsp;if foo is B { // Optional\<B\>.some is added to foo's type stack  
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > &nbsp;&nbsp;&nbsp;&nbsp;} // Optional\<B\>.some is popped from foo's type stack  
> > &nbsp;&nbsp;&nbsp;&nbsp;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
> > 
> > Here we can see that the extra condition (foo == bar) does not prevent  
> > type-narrowing, as the variable bar cannot be nil so both conditions  
> > require a type of Optional\<A\>.some as a minimum.
> > 
> > In this example foo is also nil at the end of both branches, thus its  
> > type can remain narrowed past this point.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context](https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context)  
> > Triggers  
> > TriggerImpact  
> > as Explicitly narrows a type with as! failing and as? narrowing to Type? instead  
> > when this is not possible.  
> > is Anywhere a type is tested will allow the type-checker to infer the new  
> > type if there was a match (and other conditions agree).  
> > case Any form of exhaustive test on an enum type allows it to be narrowed  
> > either to that case or the opposite, e.g- foo != nil eliminates .none,  
> > leaving only .some as the type, which can then be implicitly unwrapped  
> > (see Enum Types above).  
> > = 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.
> > 
> > There may be other triggers that should be considered.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact](https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact)  
> > on existing code
> > 
> > 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 nil value.
> > 
> > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives](https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives)  
> > considered  
> > 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.
> > 
> > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > swift-evolution mailing list  
> > swift-evolution@swift.org  
> > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)
> > 
> > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > swift-evolution mailing list  
> > swift-evolution@swift.org  
> > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)

---

<div class="post-metadata">

**Author:** ![Haravikk3](https://avatars.discourse-cdn.com/v4/letter/h/df705f/32.png) [@Haravikk3](https://forums.swift.org/u/Haravikk3)\
**Post date:** [November 3, 2016, 9:43pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/5 "2016-11-03T21:43:16Z")

</div>

> For the motivating examples, there is already a much cleaner solution:  
> (foo as? B)?.someMethodSpecificToB()

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.

> Aside from any implementation concerns, this proposal substantially increases the cognitive load on developers.

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.

Really the burden is no different to the techniques that already exist; if you've tested a value with `is` then you 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.

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.

> 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:
> 
> var foo:A? = A()  
> if foo != nil {  
> &nbsp;&nbsp;&nbsp;foo.someMethod() // Bad news!  
> &nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Bad news!  
> }

Actually the issue is 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.

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.

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.

> **···**
>
> > On 3 Nov 2016, at 19:23, Nevin Brackett-Rozinsky \<nevin.brackettrozinsky@gmail.com\> wrote:
> 
> > > On Thu, Nov 3, 2016 at 1:04 PM, Haravikk via swift-evolution \<swift-evolution@swift.org \<mailto:swift-evolution@swift.org\>\> wrote:
> > > 
> > > 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.  
> > > 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.
> > > 
> > > Type Narrowing
> > > 
> > > Proposal: SE-NNNN \<[https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt](https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt);  
> > > Author: Haravikk \<[https://github.com/haravikk&gt](https://github.com/haravikk&gt);  
> > > Status: Awaiting review  
> > > Review manager: TBD  
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt;Introduction](https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt;Introduction)
> > > 
> > > This proposal is to introduce type-narrowing to Swift, enabling the type-checker to automatically infer a narrower type from context such as conditionals.
> > > 
> > > Swift-evolution thread: Discussion thread topic for that proposal \<[http://news.gmane.org/gmane.comp.lang.swift.evolution&gt](http://news.gmane.org/gmane.comp.lang.swift.evolution&gt);  
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt;Motivation](https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt;Motivation)
> > > 
> > > 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:
> > > 
> > > let foo:A = B() // B extends A  
> > > if foo is B {  
> > > &nbsp;&nbsp;&nbsp;&nbsp;(foo as B).someMethodSpecificToB()  
> > > }  
> > > But also in the case of unwrapping of optionals:
> > > 
> > > var foo:A? = A()  
> > > if var foo = foo { // foo is now unwrapped and shadowed  
> > > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > > &nbsp;&nbsp;&nbsp;&nbsp;foo!.someMutatingMethod() // Can't be done  
> > > }  
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed) solution
> > > 
> > > 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.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed) design
> > > 
> > > 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.
> > > 
> > > 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.
> > > 
> > > Here are the above examples re-written to take advantage of type-narrowing:
> > > 
> > > let foo:A = B() // B extends A  
> > > if foo is B { // B is added to foo's type stack  
> > > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > > }  
> > > // B is popped from foo's type stack  
> > > var foo:A? = A()  
> > > if foo != nil { // Optional\<A\>.some is added to foo's type stack  
> > > &nbsp;&nbsp;&nbsp;foo.someMethod()  
> > > &nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Can modify mutable original  
> > > }  
> > > // Optional\<A\>.some is popped from foo's type stack  
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum](https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum) Types
> > > 
> > > As seen in the simple optional example, to implement optional support each case in an enum is considered be a unique sub-type of the enum itself, thus allowing narrowing to nil (.none) and non-nil (.some) types.
> > > 
> > > This behaviour actually enables some other useful behaviours, specifically, if a value is known to be either nil or non-nil 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:
> > > 
> > > 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  
> > > 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 Optional\<A\>.some satisfies the type requirements of var foo:A?, thus it is implicitly unwrapped for use. For general enums this likely means no cases are implicitly unwrapped unless using a type of Any.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type](https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type) Widening
> > > 
> > > 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.
> > > 
> > > For example:
> > > 
> > > let foo:A? = A()  
> > > if (foo != nil) { // Type of foo is Optional\<A\>.some  
> > > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > > &nbsp;&nbsp;&nbsp;&nbsp;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  
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple](https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple) Conditions and Branching
> > > 
> > > When dealing with complex conditionals or branches, all paths must agree on a common type for narrowing to occur. For example:
> > > 
> > > 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  
> > > &nbsp;&nbsp;&nbsp;&nbsp;if foo is B { // Optional\<B\>.some is added to foo's type stack  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > > &nbsp;&nbsp;&nbsp;&nbsp;} // Optional\<B\>.some is popped from foo's type stack  
> > > &nbsp;&nbsp;&nbsp;&nbsp;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  
> > > Here we can see that the extra condition (foo == bar) does not prevent type-narrowing, as the variable bar cannot be nil so both conditions require a type of Optional\<A\>.some as a minimum.
> > > 
> > > In this example foo is also nil at the end of both branches, thus its type can remain narrowed past this point.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context](https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context) Triggers
> > > 
> > > Trigger Impact  
> > > as Explicitly narrows a type with as! failing and as? narrowing to Type? instead when this is not possible.  
> > > is Anywhere a type is tested will allow the type-checker to infer the new type if there was a match (and other conditions agree).  
> > > case Any form of exhaustive test on an enum type allows it to be narrowed either to that case or the opposite, e.g- foo != nil eliminates .none, leaving only .some as the type, which can then be implicitly unwrapped (see Enum Types above).  
> > > = 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.  
> > > There may be other triggers that should be considered.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact](https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact) on existing code
> > > 
> > > 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 nil value.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives](https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives) considered
> > > 
> > > 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.
> > > 
> > > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > > swift-evolution mailing list  
> > > swift-evolution@swift.org \<mailto:swift-evolution@swift.org\>  
> > > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)

---

<div class="post-metadata">

**Author:** ![Chris\_Lattner](https://avatars.discourse-cdn.com/v4/letter/c/bc8723/32.png) [@Chris\_Lattner](https://forums.swift.org/u/Chris_Lattner)\
**Post date:** [November 7, 2016, 3:52am UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/6 "2016-11-07T03:52:59Z")

</div>

FWIW, we have specifically considered something like this proposal in the past. You didn’t mention it, but the ?: operator is a specific example that frequently comes up. People often expect to be able to do something like:

&nbsp;&nbsp;… = foo is B ? foo.bMethod() : …

which is the same sort of flow sensitive type refinement as you’re proposing here. This topic also came up in the design discussion around #available (which shipped in Swift 2, but was discussed much earlier), because unavailable decls could be available as optionals when not specifically checked for.

This is just MHO, but while I have been in favor of this in the (distant) past, I became convinced that this would be a bad idea when it comes to code maintenance over the long term. With our current (intentional shadowing based) design, you can always jump to the definition of a value to see where it was defined, and definitions always specify a type. Introducing flow senstitive type refinement breaks this model because the type of a decl depends not just on its declaration, but on a potentially arbitrary number of imperative checks that occur between the declaration and the use. This can make it much more difficult to understand code.

-Chris

> **···**
>
> > On Nov 3, 2016, at 10:04 AM, Haravikk via swift-evolution \<swift-evolution@swift.org\> wrote:
> > 
> > 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.  
> > 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 class="post-metadata">

**Author:** ![hartbit](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hartbit/32/738_2.png) [@hartbit](https://forums.swift.org/u/hartbit)\
**Post date:** [November 9, 2016, 6:51am UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/7 "2016-11-09T06:51:10Z")

</div>

Very well said. I think this is perhaps the number one complaint I have about the proposal.

> **···**
>
> > On 3 Nov 2016, at 20:23, Nevin Brackett-Rozinsky via swift-evolution \<swift-evolution@swift.org\> wrote:
> > 
> > This looks like a lot of complexity for very little gain.
> > 
> > Aside from any implementation concerns, this proposal substantially increases the cognitive load on developers. To figure out what a piece of code means, someone reading it will have to mentally keep track of a “type stack” for every variable. That is the opposite of “clarity at the point of use”.

---

<div class="post-metadata">

**Author:** ![Nevin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nevin/32/617_2.png) [@Nevin](https://forums.swift.org/u/Nevin)\
**Post date:** [November 3, 2016, 10:56pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/8 "2016-11-03T22:56:16Z")

</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:

protocol A { var x: Int {get} }  
struct B: A { var x: Int }  
struct C { var a: A }  
var c = C(a: B(x: 4))

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.

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.

We would prefer to write something like the following:

(c.a as? B)?.x = 12

But that does not currently work, resulting in the error “Cannot assign to  
immutable expression of type 'Int'”.

Will the proposed type-narrowing feature provide a simple way to mutate  
“c.a” in place, contingent upon its being of type B?

How does it compare to an alternative such as inout return values, which  
could preserve mutability in the above?

• • •

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:

if rebind c.a as B {  
&nbsp;&nbsp;&nbsp;&nbsp;c.a.x = 12  
}

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.

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.

Nevin

> **···**
>
> On Thu, Nov 3, 2016 at 5:43 PM, Haravikk \<swift-evolution@haravikk.me\> wrote:
> 
> > On 3 Nov 2016, at 19:23, Nevin Brackett-Rozinsky \< \> nevin.brackettrozinsky@gmail.com\> wrote:  
> > For the motivating examples, there is already a much cleaner solution:  
> > (foo as? B)?.someMethodSpecificToB()
> > 
> > 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.
> > 
> > Aside from any implementation concerns, this proposal substantially  
> > increases the cognitive load on developers.
> > 
> > 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.
> > 
> > Really the burden is no different to the techniques that already exist; if  
> > you've tested a value with `is` then \*you\* 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.
> > 
> > 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.
> > 
> > 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:
> > 
> > var foo:A? = A()  
> > if foo != nil {  
> > &nbsp;&nbsp;&nbsp;foo.someMethod() // Bad news!  
> > &nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Bad news!  
> > }
> > 
> > Actually the issue \*is\* 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.
> > 
> > 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.
> > 
> > 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.
> > 
> > On Thu, Nov 3, 2016 at 1:04 PM, Haravikk via swift-evolution \< \> swift-evolution@swift.org\> wrote:
> > 
> > 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.  
> > > 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.
> > > 
> > > Type Narrowing
> > > 
> > > &nbsp;&nbsp;&nbsp;- Proposal: SE-NNNN  
> > > &nbsp;&nbsp;&nbsp;\<[https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt](https://github.com/Haravikk/swift-evolution/blob/master/proposals/NNNN-type-narrowing.md&gt);  
> > > &nbsp;&nbsp;&nbsp;- Author: Haravikk \<[https://github.com/haravikk&gt](https://github.com/haravikk&gt);  
> > > &nbsp;&nbsp;&nbsp;- Status: Awaiting review  
> > > &nbsp;&nbsp;&nbsp;- Review manager: TBD
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt](https://github.com/Haravikk/swift-evolution/tree/master/proposals#introduction&gt);  
> > > Introduction
> > > 
> > > This proposal is to introduce type-narrowing to Swift, enabling the  
> > > type-checker to automatically infer a narrower type from context such as  
> > > conditionals.
> > > 
> > > Swift-evolution thread: Discussion thread topic for that proposal  
> > > \<[http://news.gmane.org/gmane.comp.lang.swift.evolution&gt](http://news.gmane.org/gmane.comp.lang.swift.evolution&gt);
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt](https://github.com/Haravikk/swift-evolution/tree/master/proposals#motivation&gt);  
> > > Motivation
> > > 
> > > 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:
> > > 
> > > let foo:A = B() // B extends A  
> > > if foo is B {  
> > > &nbsp;&nbsp;&nbsp;&nbsp;(foo as B).someMethodSpecificToB()  
> > > }
> > > 
> > > But also in the case of unwrapping of optionals:
> > > 
> > > var foo:A? = A()  
> > > if var foo = foo { // foo is now unwrapped and shadowed  
> > > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > > &nbsp;&nbsp;&nbsp;&nbsp;foo!.someMutatingMethod() // Can't be done  
> > > }
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#proposed-solution&gt;Proposed)  
> > > solution
> > > 
> > > 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.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed](https://github.com/Haravikk/swift-evolution/tree/master/proposals#detailed-design&gt;Detailed)  
> > > design
> > > 
> > > 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.
> > > 
> > > 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.
> > > 
> > > Here are the above examples re-written to take advantage of  
> > > type-narrowing:
> > > 
> > > let foo:A = B() // B extends A  
> > > if foo is B { // B is added to foo's type stack  
> > > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > > }  
> > > // B is popped from foo's type stack
> > > 
> > > var foo:A? = A()  
> > > if foo != nil { // Optional\<A\>.some is added to foo's type stack  
> > > &nbsp;&nbsp;&nbsp;foo.someMethod()  
> > > &nbsp;&nbsp;&nbsp;foo.someMutatingMethod() // Can modify mutable original  
> > > }  
> > > // Optional\<A\>.some is popped from foo's type stack
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum](https://github.com/Haravikk/swift-evolution/tree/master/proposals#enum-types&gt;Enum)  
> > > Types
> > > 
> > > As seen in the simple optional example, to implement optional support  
> > > each case in an enum is considered be a unique sub-type of the enum  
> > > itself, thus allowing narrowing to nil (.none) and non-nil (.some) types.
> > > 
> > > This behaviour actually enables some other useful behaviours,  
> > > specifically, if a value is known to be either nil or non-nil 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:
> > > 
> > > 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
> > > 
> > > 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  
> > > Optional\<A\>.some satisfies the type requirements of var foo:A?, thus it  
> > > is implicitly unwrapped for use. For general enums this likely means no  
> > > cases are implicitly unwrapped unless using a type of Any.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type](https://github.com/Haravikk/swift-evolution/tree/master/proposals#type-widening&gt;Type)  
> > > Widening
> > > 
> > > 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.
> > > 
> > > For example:
> > > 
> > > let foo:A? = A()  
> > > if (foo != nil) { // Type of foo is Optional\<A\>.some  
> > > &nbsp;&nbsp;&nbsp;&nbsp;foo.someMethod()  
> > > &nbsp;&nbsp;&nbsp;&nbsp;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
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple](https://github.com/Haravikk/swift-evolution/tree/master/proposals#multiple-conditions-and-branching&gt;Multiple)  
> > > Conditions and Branching
> > > 
> > > When dealing with complex conditionals or branches, all paths must agree  
> > > on a common type for narrowing to occur. For example:
> > > 
> > > 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  
> > > &nbsp;&nbsp;&nbsp;&nbsp;if foo is B { // Optional\<B\>.some is added to foo's type stack  
> > > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foo.someMethodSpecificToB()  
> > > &nbsp;&nbsp;&nbsp;&nbsp;} // Optional\<B\>.some is popped from foo's type stack  
> > > &nbsp;&nbsp;&nbsp;&nbsp;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
> > > 
> > > Here we can see that the extra condition (foo == bar) does not prevent  
> > > type-narrowing, as the variable bar cannot be nil so both conditions  
> > > require a type of Optional\<A\>.some as a minimum.
> > > 
> > > In this example foo is also nil at the end of both branches, thus its  
> > > type can remain narrowed past this point.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context](https://github.com/Haravikk/swift-evolution/tree/master/proposals#context-triggers&gt;Context)  
> > > Triggers  
> > > TriggerImpact  
> > > as Explicitly narrows a type with as! failing and as? narrowing to Type? instead  
> > > when this is not possible.  
> > > is Anywhere a type is tested will allow the type-checker to infer the  
> > > new type if there was a match (and other conditions agree).  
> > > case Any form of exhaustive test on an enum type allows it to be  
> > > narrowed either to that case or the opposite, e.g- foo != nil eliminates  
> > > .none, leaving only .some as the type, which can then be implicitly  
> > > unwrapped (see Enum Types above).  
> > > = 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.
> > > 
> > > There may be other triggers that should be considered.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact](https://github.com/Haravikk/swift-evolution/tree/master/proposals#impact-on-existing-code&gt;Impact)  
> > > on existing code
> > > 
> > > 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 nil value.
> > > 
> > > \<[https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives](https://github.com/Haravikk/swift-evolution/tree/master/proposals#alternatives-considered&gt;Alternatives)  
> > > considered  
> > > 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.
> > > 
> > > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > > swift-evolution mailing list  
> > > swift-evolution@swift.org  
> > > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)

---

<div class="post-metadata">

**Author:** ![Haravikk3](https://avatars.discourse-cdn.com/v4/letter/h/df705f/32.png) [@Haravikk3](https://forums.swift.org/u/Haravikk3)\
**Post date:** [November 6, 2016, 4:37pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/9 "2016-11-06T16:37:40Z")

</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:
> 
> protocol A { var x: Int {get} }  
> struct B: A { var x: Int }  
> struct C { var a: A }  
> var c = C(a: B(x: 4))
> 
> 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.
> 
> 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.
> 
> We would prefer to write something like the following:
> 
> (c.a as? B)?.x = 12
> 
> But that does not currently work, resulting in the error “Cannot assign to immutable expression of type 'Int'”.
> 
> Will the proposed type-narrowing feature provide a simple way to mutate “c.a” in place, contingent upon its being of type B?  
> How does it compare to an alternative such as inout return values, which could preserve mutability in the above?

That's a good example, and yes it should be possible for type-narrowing to simplify stuff like this, I'll add a section on that I should probably go into more detail on how I intend working with narrowed mutable values to work, as for the advantage vs inout it's really just a matter of simplicity I think; using type narrowing should allow it to just work without having to pass to methods or design the API specifically for that kind of thing.

> 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:
> 
> if rebind c.a as B {  
> &nbsp;&nbsp;&nbsp;&nbsp;c.a.x = 12  
> }

I don't see what's really gained by making it explicit vs implicit; if the condition was c.a is B then I'm not sure how that's any less clear?

> 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.

I'd say that in a sense enums are types, after all they can have unique associated value types of their own. Really though my intent is primarily to support optionals, but I figure it makes sense to try and do it from the perspective of general purpose enums since that's all optionals really are anyway.

Also, I've been thinking about thread safety and it occurs to me that type narrowing could actually be of significant benefit to it, rather than a detriment.

Consider:

&nbsp;&nbsp;struct Foo { var value:Int }  
&nbsp;&nbsp;struct Bar { var foo?:Foo }

&nbsp;&nbsp;var a = Foo(value: 5)  
&nbsp;&nbsp;var b = Bar(foo: a)

&nbsp;&nbsp;b.foo.value = 10

Now, in this case we're only dealing with structs, and we know that b.foo has a value, thus b.foo.value is nice and safe; in fact no force unwrapping needs to occur behind the scenes, it can optimise away completely.

Instead, let's assume Bar is a class we're handling in a potentially shared way:

&nbsp;&nbsp;class Bar { var foo?:Foo }

&nbsp;&nbsp;func myMethod(bar:Bar) {  
&nbsp;&nbsp;&nbsp;&nbsp;b.foo = new Foo(5) // We now know that b.foo shouldn't be nil  
&nbsp;&nbsp;&nbsp;&nbsp;b.foo!.value = 10  
&nbsp;&nbsp;}

In this case, since Bar is a class we don't have total control over, we can't be certain that b.foo is non-nil when we try to modify it a second time; as a result, type-narrowing won't occur (because it's a class from another scope), however, because the type-checker knows that bar.foo should have a value, we can actually issue a more informative error message if force unwrapping fails, e.g- concurrent modification error.

In other words, we can potentially use it to help detect concurrency issues. It's another thing that's going to require a section on the proposal though, I have a feeling it's going to get pretty big!

> **···**
>
> > On 3 Nov 2016, at 22:56, Nevin Brackett-Rozinsky \<nevin.brackettrozinsky@gmail.com\> wrote:

---

<div class="post-metadata">

**Author:** ![Jean-Daniel](https://avatars.discourse-cdn.com/v4/letter/j/977dab/32.png) [@Jean-Daniel](https://forums.swift.org/u/Jean-Daniel)\
**Post date:** [November 7, 2016, 7:50am UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/10 "2016-11-07T07:50:16Z")

</div>

For the case of the ?: operator, I think it can be replaced by a ?? operator for most cases:

… = (foo as B)?.bMethod() ?? …

> **···**
>
> > Le 7 nov. 2016 à 04:52, Chris Lattner via swift-evolution \<swift-evolution@swift.org\> a écrit :
> > 
> > > On Nov 3, 2016, at 10:04 AM, Haravikk via swift-evolution \<swift-evolution@swift.org\> wrote:
> > > 
> > > 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.  
> > > 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.
> > 
> > FWIW, we have specifically considered something like this proposal in the past. You didn’t mention it, but the ?: operator is a specific example that frequently comes up. People often expect to be able to do something like:
> > 
> > &nbsp;&nbsp;… = foo is B ? foo.bMethod() : …

---

<div class="post-metadata">

**Author:** ![Haravikk3](https://avatars.discourse-cdn.com/v4/letter/h/df705f/32.png) [@Haravikk3](https://forums.swift.org/u/Haravikk3)\
**Post date:** [November 7, 2016, 11:34am UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/11 "2016-11-07T11:34:31Z")

</div>

This seems like more of a challenge for the IDE; if it can tap into the type-checker then it can determine what the narrowed type is at any given point in your code, indeed I would expect it to for the purposes of auto-completion anyway. I know you don't necessarily want a language that's reliant on good IDE support, but if you're doing something complex enough where this would become a problem and NOT using a good IDE then it seems kind of like a self-inflicted problem to me.

Even so there's nothing in this feature that would prevent you from using shadowing if you want to, for example if a block is especially large and you feel it adds clarity.

Actually though I'd say that for maintenance narrowing may be better, as it can clarify what a type is supposed to be at a given point, and if you break the narrowing then you'll create errors and warning that show you how you've changed the meaning of the code. Consider for example:

&nbsp;&nbsp;func doSomething(value:Int?) {  
&nbsp;&nbsp;&nbsp;&nbsp;if (value == nil) { value = 5 } // value is narrow to Optional\<Int\>.some

&nbsp;&nbsp;&nbsp;&nbsp;// Lots of really important code that never causes value to become nil

&nbsp;&nbsp;&nbsp;&nbsp;print(value!.description)  
&nbsp;&nbsp;}

Say you come back later and decide to remove the conditional at the top, now that value!, though a fair assumption at the time, can cause a runtime failure. With narrowing however you wouldn't have had to force unwrap because of the known non-nil value, but your change will break that, resulting in an error that forces you to fix it.

I'm still struggling how best to phrase my motivation section; so far I seem to have an increasingly large grab-bag of individual problems that type-narrowing can solve, with no way to put it more succinctly.

> **···**
>
> > On 7 Nov 2016, at 03:52, Chris Lattner \<clattner@apple.com\> wrote:  
> > Introducing flow senstitive type refinement breaks this model because the type of a decl depends not just on its declaration, but on a potentially arbitrary number of imperative checks that occur between the declaration and the use. This can make it much more difficult to understand code.

---

<div class="post-metadata">

**Author:** ![Nevin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nevin/32/617_2.png) [@Nevin](https://forums.swift.org/u/Nevin)\
**Post date:** [November 7, 2016, 5:44pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/12 "2016-11-07T17:44:47Z")

</div>

The more I think about it, the more I realize what I actually want is an  
“alias” or “view” or “lens” that will let me give a new name to an existing  
entity, without copying or moving it.

This is useful in general if you need to work “down the rabbit hole”, so  
you could write something like:

alias size = anArray[idx].someProperty.size  
// Work with “size” as a shorthand, including mutations

In the situation from my previous example, where we only want to take  
action when the nested property is a certain type, it might look like:

if alias b = c.a as? B {  
&nbsp;&nbsp;&nbsp;&nbsp;b.x = 12  
}

I think this sort of feature is far more versatile than type-narrowing, and  
it retains the clarity of type information in source code.

Nevin

> **···**
>
> On Sun, Nov 6, 2016 at 10:52 PM, Chris Lattner via swift-evolution \< swift-evolution@swift.org\> wrote:
> 
> > \> On Nov 3, 2016, at 10:04 AM, Haravikk via swift-evolution \< \> swift-evolution@swift.org\> wrote:  
> > \>  
> > \> 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.  
> > \> 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.
> > 
> > FWIW, we have specifically considered something like this proposal in the  
> > past. You didn’t mention it, but the ?: operator is a specific example  
> > that frequently comes up. People often expect to be able to do something  
> > like:
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;… = foo is B ? foo.bMethod() : …
> > 
> > which is the same sort of flow sensitive type refinement as you’re  
> > proposing here. This topic also came up in the design discussion around  
> > #available (which shipped in Swift 2, but was discussed much earlier),  
> > because unavailable decls could be available as optionals when not  
> > specifically checked for.
> > 
> > This is just MHO, but while I have been in favor of this in the (distant)  
> > past, I became convinced that this would be a bad idea when it comes to  
> > code maintenance over the long term. With our current (intentional  
> > shadowing based) design, you can always jump to the definition of a value  
> > to see where it was defined, and definitions always specify a type.  
> > Introducing flow senstitive type refinement breaks this model because the  
> > type of a decl depends not just on its declaration, but on a potentially  
> > arbitrary number of imperative checks that occur between the declaration  
> > and the use. This can make it much more difficult to understand code.
> > 
> > -Chris  
> > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > swift-evolution mailing list  
> > swift-evolution@swift.org  
> > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)

---

<div class="post-metadata">

**Author:** ![Rien](https://avatars.discourse-cdn.com/v4/letter/r/0ea827/32.png) [@Rien](https://forums.swift.org/u/Rien)\
**Post date:** [November 9, 2016, 9:09am UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/13 "2016-11-09T09:09:16Z")

</div>

Exactly.  
-1.

Regards,  
Rien

Site: [http://balancingrock.nl](http://balancingrock.nl)  
Blog: [http://swiftrien.blogspot.com](http://swiftrien.blogspot.com)  
Github: [Swiftrien (Rien) · GitHub](http://github.com/Swiftrien)  
Project: [http://swiftfire.nl](http://swiftfire.nl)

> **···**
>
> > On 09 Nov 2016, at 07:51, David Hart via swift-evolution \<swift-evolution@swift.org\> wrote:
> > 
> > > On 3 Nov 2016, at 20:23, Nevin Brackett-Rozinsky via swift-evolution \<swift-evolution@swift.org\> wrote:
> > > 
> > > This looks like a lot of complexity for very little gain.
> > > 
> > > Aside from any implementation concerns, this proposal substantially increases the cognitive load on developers. To figure out what a piece of code means, someone reading it will have to mentally keep track of a “type stack” for every variable. That is the opposite of “clarity at the point of use”.
> > 
> > Very well said. I think this is perhaps the number one complaint I have about the proposal.

---

<div class="post-metadata">

**Author:** ![Charlie\_Monroe1](https://avatars.discourse-cdn.com/v4/letter/c/d6d6ee/32.png) [@Charlie\_Monroe1](https://forums.swift.org/u/Charlie_Monroe1)\
**Post date:** [November 7, 2016, 11:58am UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/14 "2016-11-07T11:58:08Z")

</div>

I'd personally not make this automatic, but require explicit action from the developer.

In case of nullability, I have previously suggested "nonnil" keyword:

let foo: String? = "Hello World"  
guard nonnil foo else {  
&nbsp;&nbsp;&nbsp;&nbsp;return  
}

In which way you explicitly request the type narrowing. Or:

let foo: Any  
guard foo as String else {  
&nbsp;&nbsp;&nbsp;&nbsp;return  
}

I.e. not using "is" which returns a boolean, but using the cast operator, which IMHO makes more sense and prevents from unintentional type narrowing...

> **···**
>
> > On Nov 7, 2016, at 12:34 PM, Haravikk via swift-evolution \<swift-evolution@swift.org\> wrote:
> > 
> > > On 7 Nov 2016, at 03:52, Chris Lattner \<clattner@apple.com \<mailto:clattner@apple.com\>\> wrote:  
> > > Introducing flow senstitive type refinement breaks this model because the type of a decl depends not just on its declaration, but on a potentially arbitrary number of imperative checks that occur between the declaration and the use. This can make it much more difficult to understand code.
> > 
> > This seems like more of a challenge for the IDE; if it can tap into the type-checker then it can determine what the narrowed type is at any given point in your code, indeed I would expect it to for the purposes of auto-completion anyway. I know you don't necessarily want a language that's reliant on good IDE support, but if you're doing something complex enough where this would become a problem and NOT using a good IDE then it seems kind of like a self-inflicted problem to me.
> > 
> > Even so there's nothing in this feature that would prevent you from using shadowing if you want to, for example if a block is especially large and you feel it adds clarity.
> > 
> > Actually though I'd say that for maintenance narrowing may be better, as it can clarify what a type is supposed to be at a given point, and if you break the narrowing then you'll create errors and warning that show you how you've changed the meaning of the code. Consider for example:
> > 
> > &nbsp;&nbsp;func doSomething(value:Int?) {  
> > &nbsp;&nbsp;&nbsp;&nbsp;if (value == nil) { value = 5 } // value is narrow to Optional\<Int\>.some
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;// Lots of really important code that never causes value to become nil
> > 
> > &nbsp;&nbsp;&nbsp;&nbsp;print(value!.description)  
> > &nbsp;&nbsp;}
> > 
> > Say you come back later and decide to remove the conditional at the top, now that value!, though a fair assumption at the time, can cause a runtime failure. With narrowing however you wouldn't have had to force unwrap because of the known non-nil value, but your change will break that, resulting in an error that forces you to fix it.
> > 
> > I'm still struggling how best to phrase my motivation section; so far I seem to have an increasingly large grab-bag of individual problems that type-narrowing can solve, with no way to put it more succinctly.  
> > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > swift-evolution mailing list  
> > swift-evolution@swift.org  
> > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)

---

<div class="post-metadata">

**Author:** ![Haravikk3](https://avatars.discourse-cdn.com/v4/letter/h/df705f/32.png) [@Haravikk3](https://forums.swift.org/u/Haravikk3)\
**Post date:** [November 7, 2016, 1:08pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/15 "2016-11-07T13:08:37Z")

</div>

Normally I'm a proponent of being more rather than less explicit, but the biggest draw of type-narrowing to me is that you're \*already\* telling the type-checker, it's really just confirming what you know automatically.

So if I do:

&nbsp;&nbsp;if foo is String {  
&nbsp;&nbsp;&nbsp;&nbsp;// Do lots of non-string stuff  
&nbsp;&nbsp;&nbsp;&nbsp;(foo as String).somethingStringSpecific  
&nbsp;&nbsp;}

On the last line of the block the type-checker is able to remind me that I already know that foo is a String, so I don't need to cast it.

Really when it comes down to it the type-narrowing never takes anything away from you; you can always handle the value as a less specific (wider) type if you want to, but if you want to treat it like a String because you know it's one, then you can do that too.

I'm concerned that if the feature had to be explicit, it would lack discoverability, and really the point is almost to get rid of the need to do things explicitly when you don't need to. It's like type inference on overdrive in a way.

I guess I just don't see why you'd think that "guard nonnil foo" is really more explicit than "guard foo != nil", in both cases you know that foo can't be nil past that point, so is a new keyword really justified?

> **···**
>
> > On 7 Nov 2016, at 11:58, Charlie Monroe \<charlie@charliemonroe.net\> wrote:
> > 
> > I'd personally not make this automatic, but require explicit action from the developer.
> > 
> > In case of nullability, I have previously suggested "nonnil" keyword:
> > 
> > let foo: String? = "Hello World"  
> > guard nonnil foo else {  
> > &nbsp;&nbsp;&nbsp;&nbsp;return  
> > }
> > 
> > In which way you explicitly request the type narrowing. Or:
> > 
> > let foo: Any  
> > guard foo as String else {  
> > &nbsp;&nbsp;&nbsp;&nbsp;return  
> > }
> > 
> > I.e. not using "is" which returns a boolean, but using the cast operator, which IMHO makes more sense and prevents from unintentional type narrowing…

---

<div class="post-metadata">

**Author:** ![Charlie\_Monroe1](https://avatars.discourse-cdn.com/v4/letter/c/d6d6ee/32.png) [@Charlie\_Monroe1](https://forums.swift.org/u/Charlie_Monroe1)\
**Post date:** [November 7, 2016, 4:29pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/16 "2016-11-07T16:29:34Z")

</div>

I'm simply worried a little about unwanted effects and additional compiler "cleverness". I'd simply much rather opt-in to it using a keyword or a slightly different syntax. And instead of

&nbsp;&nbsp;if foo is String { ... }

I'd prefer

&nbsp;&nbsp;if foo as? String { ... }

which is syntactically closer to

&nbsp;&nbsp;if let foo = foo as? String { ... }

which is generally what we're after.

Also, it would maintain code compatibility. The current proposal would change semantics of the code - mostly when comparing to nil.

Xcode's migration is "nice", but I'd like to point out that migration to Swift 3 of my project took 6 hours (!) and I spent almost 2 more days manually changing what the migrator didn't manage to do on its own. And that was one of my projects. I really don't want to go through this once more.

Not to mention the already growing non-macOS base of Swift users.

I know now is the time for the last incompatible changes, but are the benefits of implicit type narrowing so great to warrant this?

> **···**
>
> > On Nov 7, 2016, at 2:08 PM, Haravikk \<swift-evolution@haravikk.me\> wrote:
> > 
> > > On 7 Nov 2016, at 11:58, Charlie Monroe \<charlie@charliemonroe.net \<mailto:charlie@charliemonroe.net\>\> wrote:
> > > 
> > > I'd personally not make this automatic, but require explicit action from the developer.
> > > 
> > > In case of nullability, I have previously suggested "nonnil" keyword:
> > > 
> > > let foo: String? = "Hello World"  
> > > guard nonnil foo else {  
> > > &nbsp;&nbsp;&nbsp;&nbsp;return  
> > > }
> > > 
> > > In which way you explicitly request the type narrowing. Or:
> > > 
> > > let foo: Any  
> > > guard foo as String else {  
> > > &nbsp;&nbsp;&nbsp;&nbsp;return  
> > > }
> > > 
> > > I.e. not using "is" which returns a boolean, but using the cast operator, which IMHO makes more sense and prevents from unintentional type narrowing…
> > 
> > Normally I'm a proponent of being more rather than less explicit, but the biggest draw of type-narrowing to me is that you're \*already\* telling the type-checker, it's really just confirming what you know automatically.
> > 
> > So if I do:
> > 
> > &nbsp;&nbsp;if foo is String {  
> > &nbsp;&nbsp;&nbsp;&nbsp;// Do lots of non-string stuff  
> > &nbsp;&nbsp;&nbsp;&nbsp;(foo as String).somethingStringSpecific  
> > &nbsp;&nbsp;}
> > 
> > On the last line of the block the type-checker is able to remind me that I already know that foo is a String, so I don't need to cast it.
> > 
> > Really when it comes down to it the type-narrowing never takes anything away from you; you can always handle the value as a less specific (wider) type if you want to, but if you want to treat it like a String because you know it's one, then you can do that too.
> > 
> > I'm concerned that if the feature had to be explicit, it would lack discoverability, and really the point is almost to get rid of the need to do things explicitly when you don't need to. It's like type inference on overdrive in a way.
> > 
> > I guess I just don't see why you'd think that "guard nonnil foo" is really more explicit than "guard foo != nil", in both cases you know that foo can't be nil past that point, so is a new keyword really justified?

---

<div class="post-metadata">

**Author:** ![Haravikk3](https://avatars.discourse-cdn.com/v4/letter/h/df705f/32.png) [@Haravikk3](https://forums.swift.org/u/Haravikk3)\
**Post date:** [November 7, 2016, 7:03pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/17 "2016-11-07T19:03:11Z")

</div>

> I'm simply worried a little about unwanted effects and additional compiler "cleverness".

I don't believe there should be any; either the type is narrowed or it isn't, if you rely on it being a type the type-checker can't verify, you'll get an error, otherwise you won't. There shouldn't be scope for anything unexpected.

> Xcode's migration is "nice", but I'd like to point out that migration to Swift 3 of my project took 6 hours (!) and I spent almost 2 more days manually changing what the migrator didn't manage to do on its own. And that was one of my projects. I really don't want to go through this once more.

I agree, but the only code that should be affected by this is code where there is unwrapping that can be determined to either be redundant, or is definitely incorrect; in the former case it will only be a warning (so you can remove force unwrapping that is no longer needed) and in the latter it will be an error because the type-checker has actually identified something that will definitely cause a run-time error.

> **···**
>
> > On 7 Nov 2016, at 16:29, Charlie Monroe \<charlie@charliemonroe.net\> wrote:

---

<div class="post-metadata">

**Author:** ![griotspeak](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/griotspeak/32/1966_2.png) [@griotspeak](https://forums.swift.org/u/griotspeak)\
**Post date:** [November 7, 2016, 7:16pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/18 "2016-11-07T19:16:51Z")

</div>

I am mostly opposed because I don't see how this could avoid being  
complicated to explain compiler magic. Making this accessible as a feature  
for our types and operations would be a challenge and doesn't look to have  
a worthwhile yield for the effort. We can accomplish most, if not all, of  
this with shadowing.

TJ

> **···**
>
> On Mon, Nov 7, 2016 at 2:03 PM, Haravikk via swift-evolution \< swift-evolution@swift.org\> wrote:
> 
> > \> On 7 Nov 2016, at 16:29, Charlie Monroe \<charlie@charliemonroe.net\> \> wrote:  
> > \> I'm simply worried a little about unwanted effects and additional  
> > compiler "cleverness".
> > 
> > I don't believe there should be any; either the type is narrowed or it  
> > isn't, if you rely on it being a type the type-checker can't verify, you'll  
> > get an error, otherwise you won't. There shouldn't be scope for anything  
> > unexpected.
> > 
> > \> Xcode's migration is "nice", but I'd like to point out that migration to  
> > Swift 3 of my project took 6 hours (!) and I spent almost 2 more days  
> > manually changing what the migrator didn't manage to do on its own. And  
> > that was one of my projects. I really don't want to go through this once  
> > more.
> > 
> > I agree, but the only code that should be affected by this is code where  
> > there is unwrapping that can be determined to either be redundant, or is  
> > definitely incorrect; in the former case it will only be a warning (so you  
> > can remove force unwrapping that is no longer needed) and in the latter it  
> > will be an error because the type-checker has actually identified something  
> > that will definitely cause a run-time error.  
> > \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_  
> > swift-evolution mailing list  
> > swift-evolution@swift.org  
> > [https://lists.swift.org/mailman/listinfo/swift-evolution](https://lists.swift.org/mailman/listinfo/swift-evolution)

---

<div class="post-metadata">

**Author:** ![Charlie\_Monroe1](https://avatars.discourse-cdn.com/v4/letter/c/d6d6ee/32.png) [@Charlie\_Monroe1](https://forums.swift.org/u/Charlie_Monroe1)\
**Post date:** [November 7, 2016, 7:31pm UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/19 "2016-11-07T19:31:04Z")

</div>

> > I'm simply worried a little about unwanted effects and additional compiler "cleverness".
> 
> I don't believe there should be any; either the type is narrowed or it isn't, if you rely on it being a type the type-checker can't verify, you'll get an error, otherwise you won't. There shouldn't be scope for anything unexpected.

True.

I'm simply worried about the compiler speed in general, that's what I meant by "cleverness". The implicit variable typing and other features of Swift already IMHO make it incredibly slow and various features analyzing the code in order to determine the correct type can slow it even further. For comparison, a 100KLOC project of mine in pure Swift takes about 8 minutes to compile, vs. 3 minutes when it was in ObjC. With no optimizations turned on.

But I agree that designing a language around the compiler speed is wrong, but I believe designing the language without taking it into account is just as wrong. It's not worth designing features that would make the compilation so slow it would render the language unusable.

Note that I have only very limited experience with compiler implementation, I've only made a few minor things with Clang a few years back, so please feel free to correct me.

> > Xcode's migration is "nice", but I'd like to point out that migration to Swift 3 of my project took 6 hours (!) and I spent almost 2 more days manually changing what the migrator didn't manage to do on its own. And that was one of my projects. I really don't want to go through this once more.
> 
> I agree, but the only code that should be affected by this is code where there is unwrapping that can be determined to either be redundant, or is definitely incorrect; in the former case it will only be a warning (so you can remove force unwrapping that is no longer needed) and in the latter it will be an error because the type-checker has actually identified something that will definitely cause a run-time error.

There are two cases:

if foo != nil {  
&nbsp;&nbsp;&nbsp;&nbsp;foo!.doSomething()  
}

Currently, accessing a non-optional value with ! produces an error:

let foo = Bar()  
foo!.doSomething() // ERROR

Second:

if foo != nil {  
&nbsp;&nbsp;&nbsp;&nbsp;// Using ? to be extra cautious, if foo is var  
&nbsp;&nbsp;&nbsp;&nbsp;foo?.doSomething()  
}

This again currently produces an error:

let foo = Bar()  
foo?.doSomething() // ERROR

Which is generally, what would semantically happen - the variable would loose it optionality. Or am I wrong?

Which would require migration, where within all scopes, where you check if a a variable is not nil, it removes all ? and !...

> **···**
>
> > On Nov 7, 2016, at 8:03 PM, Haravikk \<swift-evolution@haravikk.me\> wrote:
> > 
> > > On 7 Nov 2016, at 16:29, Charlie Monroe \<charlie@charliemonroe.net\> wrote:

---

<div class="post-metadata">

**Author:** ![Haravikk3](https://avatars.discourse-cdn.com/v4/letter/h/df705f/32.png) [@Haravikk3](https://forums.swift.org/u/Haravikk3)\
**Post date:** [November 8, 2016, 8:43am UTC](https://forums.swift.org/t/proposal-type-narrowing/4433/20 "2016-11-08T08:43:53Z")

</div>

> There are two cases:
> 
> if foo != nil {  
> &nbsp;&nbsp;&nbsp;&nbsp;foo!.doSomething()  
> }
> 
> Currently, accessing a non-optional value with ! produces an error:
> 
> let foo = Bar()  
> foo!.doSomething() // ERROR
> 
> Second:
> 
> if foo != nil {  
> &nbsp;&nbsp;&nbsp;&nbsp;// Using ? to be extra cautious, if foo is var  
> &nbsp;&nbsp;&nbsp;&nbsp;foo?.doSomething()  
> }
> 
> This again currently produces an error:
> 
> let foo = Bar()  
> foo?.doSomething() // ERROR
> 
> Which is generally, what would semantically happen - the variable would loose it optionality. Or am I wrong?

I probably haven't clarified well enough but under type-narrowing these would be warnings rather than errors; i.e- the general type of foo is still Optional, the type-checker merely knows that it can't be nil at that point, so would inform you that the ? or ! are unnecessary.

This is what I was trying to get at in the type-widening section; basically, if you have a variable whose type is narrowed, but do something that makes no sense for the narrowed type, then the type is widened until either a match is found or it can't go any wider (producing an error as normal).

So in your examples foo is Optional\<Bar\>.some, as a result the ? and ! operators make no sense, so the type is widened back to Optional\<Bar\> where it does make sense and the code compiles, but a warning is produced to inform you that you don't need to use those operators.

> **···**
>
> > On 7 Nov 2016, at 19:31, Charlie Monroe \<charlie@charliemonroe.net\> wrote:
> 
> > On 7 Nov 2016, at 19:31, Charlie Monroe \<charlie@charliemonroe.net\> wrote:  
> > I agree that designing a language around the compiler speed is wrong, but I believe designing the language without taking it into account is just as wrong. It's not worth designing features that would make the compilation so slow it would render the language unusable.
> > 
> > Note that I have only very limited experience with compiler implementation, I've only made a few minor things with Clang a few years back, so please feel free to correct me.
> 
> I'm not that familiar with the actual architecture either, but narrowing \*should\* be fairly simple; basically any time the compiler hits a condition or statement defined as a narrowing trigger, it pops the new narrower type onto a stack of types for that variable (in that branch). Now whenever the compiler reaches another statement for that variable (method call etc.) it resolves it first against the narrowest type, otherwise it goes up the stack (widening) till it finds a match or fails.
> 
> When a branch closes with a stack of types, the compiler will compare to other branches to see which type is the narrowest that they have in common; this is actually fairly simple (shorten the stack for each branch to the length of the shortest stack, then discard elements until the current one is a match for all branches, thus you now know what the narrowest type is past that point).
> 
> So, for types that never narrow there should be no speed difference, while for narrowed types there shouldn't be much of a difference, as these stacks of types shouldn't get very large in most cases (I'd expect anything more than three to be pretty rare).

[Next page](https://forums.swift.org/t/proposal-type-narrowing/4433.md?page=2)
