<div dir="ltr">Just to clarify, the proposal doesn&#39;t suggest to allow the associated value to be used as a subtype of the enum.<div><br></div><div><font face="monospace, monospace">enum Result&lt;T&gt; { case .success(T), .error(Error) }</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">func foo(_ x: Result&lt;Int&gt;) { /* ... */ }</font></div><div><font face="monospace, monospace">func bar(_ x: Result&lt;Int&gt;.success) { /* ... */ }<br></font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">// Not this:</font></div><div><font face="monospace, monospace">foo(5)</font></div><div><font face="monospace, monospace">bar(5)</font></div><div><font face="monospace, monospace">// But rather:</font></div><div><font face="monospace, monospace">foo(.success(5))</font></div><div><font face="monospace, monospace">bar(.success(5))</font></div><div><br></div><div>Effectively, Result&lt;T&gt;.success would behave like a struct that is a subtype of Result&lt;T&gt;.</div><div><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Feb 21, 2017 at 12:50 PM, Joe Groff via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><br><div><div><div class="m_-5758775723457976545h5"><blockquote type="cite"><div>On Feb 20, 2017, at 1:53 PM, Matthew Johnson &lt;<a href="mailto:matthew@anandabits.com" target="_blank">matthew@anandabits.com</a>&gt; wrote:</div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline"><div><blockquote type="cite" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline">On Feb 20, 2017, at 3:22 PM, Joe Groff &lt;<a href="mailto:jgroff@apple.com" target="_blank">jgroff@apple.com</a>&gt; wrote:</div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><blockquote type="cite"><div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline">On Feb 20, 2017, at 1:04 PM, Matthew Johnson &lt;<a href="mailto:matthew@anandabits.com" target="_blank">matthew@anandabits.com</a>&gt; wrote:</div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline"><div><blockquote type="cite" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline">On Feb 20, 2017, at 2:38 PM, Joe Groff &lt;<a href="mailto:jgroff@apple.com" target="_blank">jgroff@apple.com</a>&gt; wrote:</div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><blockquote type="cite"><div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline">On Feb 20, 2017, at 7:32 AM, Matthew Johnson via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:</div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><blockquote type="cite"><div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline">On Feb 20, 2017, at 12:40 AM, Niels Andriesse via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:</div><br class="m_-5758775723457976545m_-7962314986248442653Apple-interchange-newline"><div><div dir="ltr"><div>I&#39;d like to discuss the possibility of treating the cases of a given enum as if they are subtypes of that enum. This seems like a natural thing to do because enum cases (especially when they have associated values) effectively define a closed set of subtypes.</div><div><br></div><div>Doing so would allow for constructions such as the following:</div><div><br></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">enum Foo {</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace"><span style="white-space:pre-wrap">  </span>case a(name: String)</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">}</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace"><br></font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">func isA(foo: Foo) -&gt; Bool {</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  // The old way:</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  if case .a = foo { return true }</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  return false</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  // The new way:</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  return foo is .a</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">}</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace"><br></font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">func printNameIfFooIsA(foo: Foo) -&gt; Bool {</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  // The old way:</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  if case let .a(name) = foo {</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">    print(name)</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  }</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  // The new way (1):</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  if let a = foo as? .a {</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">    print(<a href="http://a.name/" target="_blank">a.name</a>)</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  }</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  // The new way (2):</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  if let name = (foo as? .a)?.name {</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">    print(name)</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">  }</font></span></div><div><span style="background-color:rgb(255,255,255)"><font face="monospace, monospace">}</font></span></div><div><br></div><div>Treating an enum&#39;s cases as its subtypes would make enums easier to work with because handling them would be syntactically the same as handling other types.</div><div><br></div><div>The pattern matching capabilities of enums wouldn&#39;t be affected by this proposal.</div><div><br></div><div>Multiple other proposals have already attempted to simplify enum handling (they have particularly focused on getting rid of &quot;if case&quot; and adding the ability to treat enum case tests as expressions), but none of the solutions presented in those proposals have worked out so far.</div><div><br></div><div>I believe that this could be the right solution to multiple enum-related problems that have been brought up repeatedly.</div></div></div></blockquote><div><br></div><div>I would like to see enum cases treated as subtypes of the enum type.  This is an interesting way to refer to the type of a case.  Unfortunately I don’t think it will work if we accept the proposal to give cases a compound name.  If we do that the name of this case becomes `a(name:)` which is not a valid type name.</div></div></div></blockquote><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">I think there are definitely places where having cases be a subtype of an enum make sense, but I don&#39;t think it makes sense for *all* cases to be subtypes. For example, with &quot;biased&quot; containers like Optional and Result, it makes sense for the &quot;right&quot; side to be a subtype and the &quot;wrong&quot; side to be explicitly constructed, IMO.  If the types of cases overlap, it would also be *ambiguous* which case ought to be constructed when the payload is converted to the enum type</div></div></blockquote><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">Identical case types would definitely be a problem but I don’t think overlapping case types are always a problem.  I imagine this conversion working the same as any other ordinary overload resolution for ad-hoc overloads.</div></div></blockquote><div><br></div><div>Conversions happen at runtime too. `0 as Any as? Either&lt;Int, Int&gt;` wouldn&#39;t have any way to tell what `Either` to form if both arms of the Either were subtype candidates. An Either&lt;T, U&gt; in &lt;T, U&gt; context can end up being bound to Either&lt;Int, Int&gt; at runtime and interacting with runtime casts that way.</div></div></div></blockquote><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">Hmm.  This is unfortunate.</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">In cases where T and U overlap and form a linear hierarchy but are not identical couldn’t the runtime determine the most direct path and choose that?</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">If the compiler prohibited cases with exactly the same types like `Either&lt;Int, Int&gt;` from being expressed statically how do these types end up getting formed dynamically?  Is there any way those operations could be failable?</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><blockquote type="cite" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br><blockquote type="cite"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><blockquote type="cite" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">—remember that enums are sums, not unions, and that&#39;s important for composability and uniform behavior with generics.<span class="m_-5758775723457976545m_-7962314986248442653Apple-converted-space"> </span></div></div></blockquote><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">I’ve always thought of enums as nominal discriminated unions.  Maybe I’m using the wrong terminology.  Can you elaborate on the difference between sums and unions?  When you say union are you talking about the kind of thing some people have brought up in the past where any members in common are automatically made available on the union type?</div></div></blockquote><div><br></div><div>Sums maintain structure whereas unions collapse it. As a sum, Optional&lt;T&gt; maintains its shape even when T = Optional&lt;U&gt;. If it were a union, T u Nil u Nil would collapse to T u Nil, losing the distinction between the inner and outer nil and leading to problems in APIs that use the outer nil to communicate meaning about some outer structure, such as asking for the `first` element of a collection of Optionals.</div></div></div></blockquote><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">Got it.  This is certainly a problem for `Optional`.</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">But sometimes this behavior of collapsing the syntactic specification to a canonical sum type would be very useful.  What is the reason we can’t have something syntactic type expressions like `Int | String`, `Int | String | String, `String | Int | String | Int`, etc all collapse to the same canonical structural sum type:</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">enum {</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">   sub case int(Int), string(String)</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">}</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">This is how I’ve been thinking about those syntactic types.  We already allow existential types to be formed using syntax that collapses to a canonical type:</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">typealias Existential1 = Protocol1 &amp; Protocol2</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">typealias Existential2 = Protocol2 &amp; Existential1 &amp; Protocol 3 &amp; Protocol1</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">typealias Existential3 = Existential1 &amp; Protocol3</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">In this example Existential1 and Existential3 are different names for the same type.</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">Is there a reason we can’t have similar syntax that collapses to a similarly canonical sum type?  If we’re going to allow case subtypes this feels to me like a very natural and useful direction. </div></div></blockquote><div><br></div></div></div><div>A couple reasons that come to mind:</div><div><br></div><div>- Most directly, we don&#39;t allow abstraction over generic constraints. `ExistentialN&lt;T, U&gt; = T &amp; U` isn&#39;t allowed. As soon as you have abstraction over either unions or intersections, type checking becomes an unbounded search problem in the worst case, since every T binding is potentially equivalent to a T1 &amp; T2 or T1 | T2 with T1 == T2 == T.</div><div><br></div><div>- Sums and unions both imply a matching branch structure in the code somewhere to handle both possibilities. If the number of actual possibilities is different in different situations, that&#39;s a source of bugs, such as the overloading of `nil` I mentioned previously. Even if you did allow generic T &amp; T types, the worst result of someone seeing that as T1 &amp; T2 is that the operations enabled through conforming to T1 and T2 map to the same conformance.</div><div><br></div><div>-Joe</div><span><br><blockquote type="cite"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">If we don’t allow it there are two problems: people have to invent a largely meaningless name for the enum and it is incompatible with any other similarly structured enum.  Neither is a significant problem but they do add (seemingly) unnecessary friction to the language.</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">I wouldn’t expect these to be widely used - they would play a similar role as tuples - but they would be very appreciated where they are used.</div><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><blockquote type="cite" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"><br></div><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important">-Joe</span></div></blockquote></div></blockquote></span></div><br></div><br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailma<wbr>n/listinfo/swift-evolution</a><br>
<br></blockquote></div><br></div></div>