<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><br><br>Sent from my iPad</div><div><br>On Feb 16, 2017, at 5:40 PM, Karl Wagner <<a href="mailto:razielim@gmail.com">razielim@gmail.com</a>> wrote:<br><br></div><blockquote type="cite"><div><meta http-equiv="Content-Type" content="text/html charset=utf-8"><br class=""><div><blockquote type="cite" class=""><div class="">On 17 Feb 2017, at 00:26, Matthew Johnson <<a href="mailto:matthew@anandabits.com" class="">matthew@anandabits.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><blockquote type="cite" class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;"><div class=""><br class="Apple-interchange-newline">On Feb 16, 2017, at 5:13 PM, Karl Wagner <<a href="mailto:razielim@gmail.com" class="">razielim@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><br class=""><div class=""><blockquote type="cite" class=""><div class="">On 17 Feb 2017, at 00:00, Karl Wagner <<a href="mailto:karl.swift@springsup.com" class="">karl.swift@springsup.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><br class=""><div class=""><blockquote type="cite" class=""><div class="">On 16 Feb 2017, at 23:44, Matthew Johnson via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">I have been thinking a lot about enums and value subtyping lately and decided to write down the ideas I’ve been thinking about. The result is a manifesto-style document that explores a broad landscape of features that could eventually lead to proposals (at the right time, of course).<br class=""><br class="">I’m presenting this document to the list now mostly because I am not sure which of these features (if any) might be relevant to ABI stability, particularly with respect to standard library APIs. I do not wish to distract the list from the focus on Swift 4, phase 1. Let’s try not to get distracted by exciting ideas that won’t be in scope until at least phase 2. Feel free to send feedback off list if you’re interested in discussing ideas that may not be relevant to Swift evolution at this time.<br class=""><br class="">Because this document covers a pretty broad range of topics it might be a good idea to start a new thread before jumping in to discussion about a specific aspect of it. Please consider doing that if it is relevant before responding directly to this thread.<br class=""><br class="">As this is a relatively large document I am only providing a link:<span class="Apple-converted-space"> </span><a href="https://gist.github.com/anandabits/5b7f8e3836387e893e3a1197a4bf144d" class="">https://gist.github.com/anandabits/5b7f8e3836387e893e3a1197a4bf144d</a><br class=""><br class="">To whet your appetite, the topics covered include:<br class=""><br class="">* Definition of value subtyping<br class=""> * Transitivity of value subtypes<br class=""> * Generic supertype constraints<br class="">* Axiomatic value subtype relationships<br class="">* Enums: Value Subtype Relationships by definition<br class=""> * Nominal case types<br class=""> * Nominal unions<br class=""> * Generic enums and Optional<br class=""> * Cases with unbound generic arguments<br class=""> * Structural Unions<br class=""> * Enum subtypes<br class=""> * Inline enum subtypes<br class=""> * Inline generic enum subtypes<br class=""> * Conditional cases (and GADTs)<br class=""> * Inline case types<br class=""> * Nominal cases with inline types<br class=""> * Case type implementation sharing<br class=""> * Shared stored properties<br class=""> * Subenum stored properties<br class=""> * Shared methods and computed properties<br class="">* User-defined case patterns<br class="">_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br class=""></div></div></blockquote></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 13.600000381469727px; margin-top: 0px; margin-bottom: 0px; line-height: 1.45; word-wrap: normal; padding: 16px; overflow: auto; background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; color: rgb(51, 51, 51);"><span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">enum</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(121, 93, 163);">IntOrString</span>: <span class="pl-e" style="box-sizing: border-box; color: rgb(121, 93, 163);"><span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">Int</span> </span>| <span class="pl-e" style="box-sizing: border-box; color: rgb(121, 93, 163);"><span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">String</span> </span>{
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">case</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">Int</span>
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">case</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">String</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(150, 152, 150);"></span>}
<span class="pl-k" style="box-sizing: border-box; color: rgb(167, 29, 93);">func</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(121, 93, 163);">takesAnonymousUnion</span>(<span class="pl-smi" style="box-sizing: border-box;"><span class="pl-en" style="box-sizing: border-box; color: rgb(121, 93, 163);">intOrString</span></span>: <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">Int</span> | <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 134, 179);">String</span>) {}</pre><div class=""><br class=""></div></div><div class=""><div class="">Haven’t been through it all, just pointing out that “Structural unions” and anonymous unions have been suggested and rejected before.</div><div class=""><br class=""></div><div class="">- Karl</div></div></div></div></blockquote><br class=""></div><div class="">(maybe I should elaborate on that, because you do point it out in the document)</div><div class=""><br class=""></div><div class="">What I mean is that the reason this was rejected before was that people should be using protocols instead, and building semantic contracts. “Int” and “String” may have a method that looks the same but behaves very differently - the union provides no guarantees about _behaviour_. Protocols *do* give you guarantees about behaviour.</div></div></div></blockquote><blockquote type="cite" class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><br class=""></div><div class="">Basically, the correct way to write this (today) is:</div><div class=""><br class=""></div><blockquote class="" style="margin: 0px 0px 0px 40px; border: none; padding: 0px;"><div class=""><font face="Courier" class="">protocol CommonIntAndStringMethods {</font></div><div class=""><font face="Courier" class=""> func doSomething()</font></div><div class=""><font face="Courier" class="">}</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">enum IntOrString {</font></div><div class=""><font face="Courier" class=""> case integer(Int)</font></div><div class=""><font face="Courier" class=""> case string(String)</font></div><div class=""><font face="Courier" class="">}</font></div><div class=""><font face="Courier" class="">extension Int: CommonIntAndStringMethods {}</font></div><div class=""><font face="Courier" class="">extension String: CommonIntAndStringMethods {}</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">func myFunc(_ x: IntOrString) {</font></div><div class=""><font face="Courier" class=""> let val: CommonIntAndStringMethods</font></div><div class=""><font face="Courier" class=""> if case .integer(let i) = x { val = i }</font></div><div class=""><font face="Courier" class=""> else if case .string(let s) = x { val = s }</font></div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class=""> val.doSomething()</font></div><div class=""><font face="Courier" class="">}</font></div></blockquote><div class=""><br class=""></div><div class="">What you are proposing looks superficially similar, but isn’t. We call “doSomething” on a single type, with guaranteed same semantics.</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; -webkit-text-stroke-width: 0px;" class=""><br class=""></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; -webkit-text-stroke-width: 0px;" class="">Thanks for pointing out this aspect of the prior discussions. This is great feedback. This part of the prior discussions had slipped my mind so I didn’t distinguish the unions I am talking about from the union types that some people have asked for in the past. I’ll update the manifesto to be more clear about this.</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; -webkit-text-stroke-width: 0px;" class=""><br class=""></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; -webkit-text-stroke-width: 0px;" class="">The intent is not to expose common methods on the union type at all. In fact all you could do with a structural union as I am defining it is attempt to downcast (or switch with cast patterns). These structural unions would be for things such as:</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; -webkit-text-stroke-width: 0px;" class=""><br class=""></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; -webkit-text-stroke-width: 0px;" class="">typealias JSONValue = None | Bool | Int | Double | 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; -webkit-text-stroke-width: 0px;" class=""><br class=""></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; -webkit-text-stroke-width: 0px;" class="">Sometimes what we require union types like this. Today we can define an enum JSONValue bit it is less elegant than it could be. We don’t get the subtype relationship with Bool, Int, Double, String, etc. This means we don’t get implicit conversion to JSONValue and we don’t have the ability to downcast from JSONValue. It also means that my JSONValue is incompatible with your JSONValue.</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; -webkit-text-stroke-width: 0px;" class=""><br class=""></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; -webkit-text-stroke-width: 0px;" class="">None of this has anything to do with any operations that might be available on any (or all) of the types making up the union. I think this makes the implementation much more tractable and avoids the semantic issues you point out.</div><br class="" 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; -webkit-text-stroke-width: 0px;"><blockquote type="cite" class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><br class=""></div><div class="">- Karl</div></div></div></blockquote></div></blockquote></div><br class=""><div class="">It’s late and I’m not really articulating myself very well, but the point I was trying (badly) to make was that you shouldn’t really care what the exact type is, just what it can _do_. Just knowing that the value is “JSONRepresentable” should be enough. You could still conditionally downcast it if there is some optimised code-path to handle specific types.</div><div class=""><br class=""></div><div class="">The enum in my previous example is superfluous. Really you would just write:</div><div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><font face="Courier" class="">func myFunc(_ x: CommonIntAndStringMethods) { </font></div><div class=""><font face="Courier" class=""> x.doSomething() </font></div><div class=""><font face="Courier" class="">}</font></div><div class=""><font face="Courier" class=""><br class=""></font></div></blockquote><div class="">With closed protocols, you would basically get the same thing as a structural union and you *would* be able to invoke methods on it directly. Today, that idealistic scenario of working at the semantic, protocol level falls down as soon as an associated type is introduced, but that will get better as we expand on protocol existentials.</div><div class=""><br class=""></div><div class="">It took some time for me to come around to that way of thinking, but I ultimately I think it’s better. As complexity grows, it becomes easier to follow why certain methods are restricted to certain types, based on their functionality.</div></div></blockquote><div><br></div><div>This is often true, but not always. When you have a small, fixed set of types an enum (or a structural union) can be a very reasonable choice. </div><div><br></div><div>In fact, I know of some very specific use cases where structural unions would be by far the best and most clear types to use. These use cases are all at the boundary between a component and it's users. A closed protocol is indeed an alternative solution. However, in these use cases the name of the protocol would not add anything meaningful to the signature and the indirection of the conformances would obscure the intent of the API, which needs to accept a heterogeneous array whose elements can be any of a small set of types.</div><div><br></div><div>While the best use cases for structural unions are somewhat narrow they do exist. And the semantics I have used to define them (value subtyping) is extremely useful in a very wide range of use cases. If we do introduce a general value subtyping facility such as the one I have described I think it would be a shame not to eventually introduce structural unions (which are a natural consequence of the semantics of value subtyping).</div><div><br></div><div>One of the things I like best about Swift is that it has a stated goal of becoming an extremely general purpose language that scales all the way from casual scripting all the way to systems programming. This means it will have features that can be used to great effect for some purposes while being completely inappropriate for others. <span style="background-color: rgba(255, 255, 255, 0);">At the end of the day, it is up to each of us as programmers to know how to evaluate the tradeoffs and make the right choice for the task at hand. </span></div><div><br></div><blockquote type="cite"><div><div class=""><br class=""></div><div class="">- Karl</div><div class=""><br class=""></div></div></blockquote></body></html>