<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">I will attach my comments to Brent’s answer because I echo many of his thoughts:<div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On 8 Mar 2017, at 05:59, Brent Royal-Gordon 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=""><blockquote type="cite" class="">On Mar 7, 2017, at 12:14 PM, Erica Sadun via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""><br class="">Because of that, I'm going to start over here, hopefully pulling in all the details<br class="">and allowing the community to provide feedback and direction. The following <br class="">gist is an amalgam of work I was discussing with Xiaodi Wu, Chris Lattner, and<br class="">David Goodine.<br class=""><br class=""><a href="https://gist.github.com/erica/aea6a1c55e9e92f843f92e2b16879b0f" class="">https://gist.github.com/erica/aea6a1c55e9e92f843f92e2b16879b0f</a><br class=""></blockquote><br class="">Treating the things separately:<br class=""><br class="">1. Introduce an `unwrap` keyword<br class=""><br class="">I'm really not convinced this pulls its own weight. Without the `let`, it doesn't make the fact that it's shadowing the original (and thus that you cannot modify it) clear; with the `let`, it introduces a new keyword people need to learn for the sake of eliding a repeated variable name.<br class=""><br class="">In the document, you state that `unwrap` "simplifies the status quo and eleminates unintended shadows", but that's not true, because the existing syntax will continue to exist and be supported. Unless we warn about *any* shadowing in an `if let` or `if case`, it will still be possible to accidentally shadow variables using these declarations.<br class=""></div></div></blockquote><div><br class=""></div><div>I’m also not convinced this is worth it. While I see one argument for this feature (following DRY principles) I see many disadvantages:</div><div><br class=""></div><div><ul class="MailOutline"><li class="">introduction of an extra keyword</li><li class="">newcomers have to learn that there are two different syntaxes for optional binding</li><li class="">less clear that shadowing is happening</li></ul></div><br class=""><blockquote type="cite" class=""><div class=""><div class="">2. Introduce an `Unwrappable` protocol<br class=""><br class="">I like the idea, but I would use a slightly different design which offers more features and lifts this from "bag of syntax" territory into representing a discrete semantic. This particular design includes several elements which depend on other proposed features:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>/// Conforming types wrap another type, creating a supertype which may or may not <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>/// contain the `Wrapped` type.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>/// <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>/// `Wrapper` types may use the `!` operator to unconditionally access the wrapped <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>/// value or the `if let` and `guard let` statements to conditionally access it. Additionally, <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>/// `Wrapped` values will be automatically converted to the `Wrapper`-conforming type <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>/// as needed, and the `is`, `as`, `as?`, and `as!` operators will treat the `Wrapped` type <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>/// as a subtype of the `Wrapper`-conforming type.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>protocol Wrapper {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// The type that this value wraps.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>associatedtype Wrapped<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// The type of error, if any, thrown when a non-wrapped value is unwrapped.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>associatedtype UnwrappingError: Error = Never<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// Creates an instance of `Self` which wraps the `Wrapped` value.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// You can call this initializer explicitly, but Swift will also insert implicit calls when <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// upcasting from `Wrapped` to `Self`.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>init(_ wrapped: Wrapped)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// Returns `true` if `Self` contains an instance of `Wrapped` which can be accessed <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// by calling `unwrapped`.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var isWrapped: Bool { get }<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// Accesses the `Wrapped` value within this instance.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// If `isWrapped` is `true`, this property will always return an instance. If it is `false`, this property <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// will throw an instance of `UnwrappingError`, or trap if `UnwrappingError` is `Never`.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var unwrapped: Wrapped { get throws<UnwrappingError> }<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// Accesses the `Wrapped` value within this instance, possibly skipping safety checks.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>/// - Precondition: `isWrapped` is `true`.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var unsafelyUnwrapped: Wrapped { get }<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>extension Wrapper {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>// Default implementation of `unsafelyUnwrapped` just calls `unwrapped`.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var unsafelyUnwrapped: Wrapped {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return try! unwrapped<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class="">The defaulting of `WrappingError` to `Never` means the error-emitting aspects of this design are additive and can be introduced later, once the necessary supporting features are introduced. The use of separate `isWrapped` and `unwrapped` properties means that `unwrapped` can implement an appropriate behavior on unwrapping failure, instead of being forced to return `nil`.<br class=""><br class="">(An alternative design would have `wrapped: Wrapped? { get }` and `unwrapped: Wrapped { get throws<UnwrappingError> }` properties, instead of `isWrapped` and `unwrapped`.)<br class=""><br class="">In this model, your example of:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>let value = try unwrap myResult // throws on `failure`<br class=""><br class="">Would instead be:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>let value = try myResult! // throws on `failure`<br class=""></div></div></blockquote><div><br class=""></div><div>Wouldn’t this be confusing for people who associate ! with crashing behaviour (in optional! and try!)</div><br class=""><blockquote type="cite" class=""><div class=""><div class="">(Actually, I'm not sure why you said this would be `unwrap`—it's not shadowing `myResult`, is it?)<br class=""><br class="">Theoretically, this exact design—or something close to it—could be used to implement subtyping:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>extension Int16: Wrapper {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>typealias Wrapped = Int8<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>init(_ wrapped: Int8) {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>self.init(exactly: wrapped)!<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var isWrapped: Bool {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return Self(exactly: Int8.min)...Self(exactly: Int8.max).contains(self)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var unwrapped: Int8 {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return Self(exactly: self)!<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class="">But this would imply that you could not only say `myInt8` where an `Int16` was needed, but also that you could write `myInt16!` where an `Int8` was needed. I'm not sure we want to overload force unwrapping like that. One possibility is that unwrapping is a refinement of subtyping:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// `Downcastable` contains the actual conversion and subtyping logic. Conforming to <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// `Downcastable` gets you `is`, `as`, `as?`, and `as!` support; it also lets you use an <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// instance of `Subtype` in contexts which want a `Supertype`.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>protocol Downcastable {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>associatedtype Subtype<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>associatedtype DowncastingError: Error = Never<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>init(upcasting subvalue: Subtype)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var canDowncast: Bool { get }<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var downcasted: Subtype { get throws<DowncastingError> }<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var unsafelyDowncasted: Subtype { get }<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// Unwrappable refines Downcastable, providing access to `!`, `if let`, etc.<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>protocol Unwrappable: Downcastable {}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>extension Unwrappable {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>var unsafelyUnwrapped: Subtype { return unsafelyDowncasted }<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class="">That would allow you to have conversions between `Int8` and `Int16`, but not to use `!` on an `Int16`.<br class=""><br class="">3. Apply `unwrap` to non-`Optional` values, and<br class="">4. Extend `for` and `switch`<br class=""><br class="">These are pretty straightforward ramifications of having both `unwrap` and `Unwrappable`. I don't like `unwrap`, but if we *do* add it, it should certainly do this.<br class=""></div></div></blockquote><div><br class=""></div><div>Same arguments as for (1)</div><br class=""><blockquote type="cite" class=""><div class=""><div class="">5. Fix Pattern Match Binding<br class=""><br class="">The `case let .someCase(x, y)` syntax is really convenient when there are a lot of variables to bind. I would suggest a fairly narrow warning: If you use a leading `let`, and some—but not all—of the variables bound by the pattern are shadowing, emit a warning. That would solve the `case let .two(newValue, oldValue)`-where-`oldValue`-should-be-a-match problem.<br class=""></div></div></blockquote><div><br class=""></div><div>Agreed. The leading let is worth it when matching many variables! I also prefer the warning solution. Forcing let at the variable site would defeat DRY principles.</div><br class=""><blockquote type="cite" class=""><div class=""><div class="">6. Simplify Complex Binding<br class=""><br class="">I'm not convinced by this. The `case` keyword provides a strong link between `if case` and `switch`/`case`; the `~=` operator doesn't do this. Unless we wanted to redesign `switch`/`case` with matching ergonomics—which, uh, we don't:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>switch value {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>~ .foo(let x): <br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>...use x...<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>...<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class="">—I don't think we should go in this direction. `for case` also has similar concerns.<br class=""><br class="">I think we'd be better off replacing the `~=` operator with something more memorable. For instance:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>extension Range {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>public func matches(_ value: Bound) -> Bool {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return contains(value)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""><br class="">Or:<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>public func isMatch<Bound: Comparable>(_ value: Bound, toCase pattern: Range<Bound>) -> Bool {<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return pattern.contains(value)<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}<br class=""></div></div></blockquote><div><br class=""></div><div>And here’s the only place I disagree with Brent. The way I see it, the `case` keyword does not provide a strong link between `if case` and `switch`/`case`, it simply confuses people who do not expect the see that keyword associated with anything else than switch. It does not scream “pattern matching” to me like ~= does.</div><div><br class=""></div><div>Erica, I think that section could contain more examples of the proposed syntax being used with cases:</div><div><br class=""></div><div>if .left(let x) ~= either { … }</div><br class=""><blockquote type="cite" class=""><div class=""><div class="">-- <br class="">Brent Royal-Gordon<br class="">Architechies<br class=""><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="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></div></blockquote></div><br class=""></div></body></html>