<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><div style="direction: inherit;">I can solve the last one! &nbsp;You often want phantom types in Swift, and they can be had albeit a bit unsafely with a "sealed conformance" to a common protocol and a set of no-case (for phantom types) or single-case (for singleton types) enums. &nbsp;This this you can do terrible things like create type-level Nats&nbsp;<a href="https://gist.github.com/CodaFi/7bb3bd00f04a9b26fd71">https://gist.github.com/CodaFi/7bb3bd00f04a9b26fd71</a>&nbsp;and things indexed by them like Fin or even recover light dependent pattern matching with type(of:).</div><div style="direction: inherit;"><br></div>~Robert Widmann</div><div><br>2016/09/27 18:59、Karl via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; のメッセージ:<br><br></div><blockquote type="cite"><div><span></span><br><blockquote type="cite"><span>On 27 Sep 2016, at 18:55, Joe Groff via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:</span><br></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><blockquote type="cite"><span>On Sep 26, 2016, at 8:51 AM, Jérôme Duquennoy via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Summary</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>The aim of this proposal is to offer a new syntax to ease some uses of enums with payload.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Situation to improve:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Enums makes it possible to have explicate typing where it was not possible before. A classic example of that is filling a dictionary with data coming from a file or a stream (json, plist, …) : the types of possible values is finite : arrays, dicts, int, double, bool or string for json for exemple.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>An enum can represent this finite range of possible types, its make the code and the API more self-documented.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Then, you have two possibilities to deal with this enum:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- using switch statements</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- using the if case syntax introduced by swift 2</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>The drawback is that those two solutions can lead to writing code with high visual complexity, even though the logic behind is pretty simple.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Consider this example of a data dictionary, that a web service could have returned:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- book</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> - title: </span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> - author:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> &nbsp;&nbsp;- name: Apple</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> &nbsp;&nbsp;- age: 40</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>We can decode this in a variable of type [String:Value], where Value is:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>enum Value {</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> case integer(value: Int)</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> case string(value: String)</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> case dict(value: [String:Value])</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> case null</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>}</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Here is a snippet of code to access the age of the author:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>if case .dict(let book) = data {</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> if case .dict(let author) = book["author"] ?? .null {</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> &nbsp;&nbsp;if case .integer(let age) = author["age"] ?? .null {</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> &nbsp;&nbsp;&nbsp;&nbsp;// now we have the age</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> &nbsp;&nbsp;}</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span> }</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>}</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>The multiple indentation levels can rapidly make this code unattractive to read, and we need to add a null case to the enum to deal with optional values.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Proposed solution:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>I suggest to add a new syntax, using the case keyword to ease access to the payload of such enums :</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>let payloadContent = case? .enumCase(variable)</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>The payloadContent variable will be an optional, that can be either nil, or contain the payload of enumCase.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>If the payload contains multiple variables, payloadContent will be a tupple.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>This syntax can accommodate an optional variable as an input. If the value of variable is nil, then payloadContent will be nil.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Only enum cases with a payload can be used with this syntax (it would make no sens for cases without a payload).</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>With that syntax, the null case of the enum can be removed, and the code to access the age becomes:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>let book = case? .dict(inputData)</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>let author = case? .dict(book?["author"])</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>let age = case? .integer(author?["age"])</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Advantages:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- It leverages the well established notion of optional, and similar logic already exists in the language (for the as? operator notably).</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- It does not add a new keyword</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- It promotes the use of enum to enforce explicit typing, which leads to more self-documenting code</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- It reduces the complexity of the code in situations such as the one of the exemple</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Drawbacks:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- It adds a third use of the case keyword. </span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- In the proposed syntax, the variable between parenthesis is not the payload, but the variable to decode. This might be disturbing, as it differs from the other syntax of enum values.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- If the payload is an optional, it is not possible to differentiate a non-matching case and a matching case a nil payload.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Alternatives:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>- Another syntax without parenthesis could be used to avoid the second drawback:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>let payload = case? .enumCase variable</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>Impact on existing code:</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>None, this is adding a new syntax</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span></span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>This proposal would have no impact on the ABI, so it probably does not fit the stage 1 of swift 4’s roadmap. But I would be glad to have your feedback, so that I can have a proposal ready once we enter stage 2.</span><br></blockquote></blockquote><blockquote type="cite"><blockquote type="cite"><span>So what your thoughts on that proposal ?</span><br></blockquote></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><span>I think it's reasonable to want an expression for extracting the payload of a enum case as an Optional. Instead of introducing a new operator, though, we could say that the cases themselves behave as Optional properties of an enum, which would allow you to say:</span><br></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><span>if let book = inputData.dict,</span><br></blockquote><blockquote type="cite"><span> &nbsp;let author = book["author"].dict,</span><br></blockquote><blockquote type="cite"><span> &nbsp;let age = author["age"].integer { ... }</span><br></blockquote><blockquote type="cite"><span></span><br></blockquote><blockquote type="cite"><span>-Joe</span><br></blockquote><blockquote type="cite"><span>_______________________________________________</span><br></blockquote><blockquote type="cite"><span>swift-evolution mailing list</span><br></blockquote><blockquote type="cite"><span><a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a></span><br></blockquote><blockquote type="cite"><span><a href="https://lists.swift.org/mailman/listinfo/swift-evolution">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br></blockquote><span></span><br><span>I was thinking the same thing. I’m finding myself writing a lot of convenience accessors of the type:</span><br><span></span><br><span>enum MyEnum&lt;T&gt; {</span><br><span> &nbsp;&nbsp;&nbsp;case stateOne</span><br><span> &nbsp;&nbsp;&nbsp;case stateTwo(Array&lt;T&gt;)</span><br><span> &nbsp;&nbsp;&nbsp;case stateThree(T)</span><br><span> &nbsp;&nbsp;&nbsp;case stateFour(Error, T)</span><br><span></span><br><span> &nbsp;&nbsp;&nbsp;var error : (Error, T)? {</span><br><span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if case .error(let r) = self { return r }</span><br><span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return .none</span><br><span> &nbsp;&nbsp;&nbsp;}</span><br><span>}</span><br><span></span><br><span>It would be nice if the compiler could generate these style of accessors, with the payload available as an optional tuple. Perhaps it would be called `var stateFourData : (Error, T)?` or something predictable.</span><br><span></span><br><span>While I’m on the subject, sometimes I want every case of an enum to be its own type, which is a subtype of the enum’s type - e.g. MyEnum&lt;Int&gt;.stateThree.self. That would allow you to keep a collection of MyEnum values which are guaranteed to all be the same case.</span><br><span>_______________________________________________</span><br><span>swift-evolution mailing list</span><br><span><a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a></span><br><span><a href="https://lists.swift.org/mailman/listinfo/swift-evolution">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br></div></blockquote></body></html>