[swift-evolution] [Discussion] Simplifying case syntax

Pyry Jahkola pyry.jahkola at iki.fi
Wed Mar 1 14:53:01 CST 2017


Instead of going into all these lengths introducing new syntax, why not simply turn it into a warning when a `case let` binding shadows an existing variable with the exact same type (which supposedly also conforms to Equatable)?

Examples, including how to silence the said warning:

    enum Value<T> { case one(T), two(T, T), three(T, T, T) }
    let example: Value<String> = .two("a", "b")
    let oldValue = "x"
    // (Besides, you probably intended `oldValue` to be a `Character` in your
    // example. Well, it's a `String` in mine.)
    if case let .two(newValue, oldValue) = example { ... }
    //                         ~~~~~~~~
    // warning: 'oldValue' shadows an existing variable of same type 'String'
    if case let .two(newValue, (oldValue)) = example { assert(oldValue == "b") }
    // Ok, adding extra parentheses silences the warning.

    if case let .one(example) = example { ... }
    // Ok, because there's no way the author would equate the `example: String`
    // in the LHS to the `example: Value<String>` of the RHS.
    let maybe: Optional = "perhaps"
    if case let maybe? = maybe { ... }
    if case let .some(maybe) = maybe { ... }
    // Again, none of these examples would be affected by the warning, because
    // the `maybe: String` bound in the `case let` has a different type than
    // the `maybe: String?` in the outer scope.

Personally, I do like the convenience that I can bind several variables with one `let` keyword in a case expression. And I can't see the appeal to making `~=`, let alone a completely new special-syntax assignment operator, more prominent in Swift.

— Pyry

> On 28 Feb 2017, at 21.01, Erica Sadun via swift-evolution <swift-evolution at swift.org> wrote:
> The following draft proposal addresses one matter of substance (eliminating edge case errors by adopting at-site conditional binding) and one of style (using the pattern match operator consistently). Its discussion was deferred from Phase 1 and remains in a fairly early stage. Your feedback will help me decide whether this is a proposal I want to keep developing or one that I should set aside and focus on other matters. Thank you. -- E
> The work-in-progress gist is here:  https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c> 
> Simplifying case syntax
> Proposal: TBD
> Author: Erica Sadun <https://github.com/erica>
> Status: TBD
> Review manager: TBD
>  <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#introduction>Introduction
> This proposal re-architects case syntax grammar to reduce potential errors and simplify unwrapping enumerations. 
> Swift-evolution thread: [Pitch] Reimagining guard case/if case <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161024/tbd.html>
>  <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#motivation>Motivation
> In its current design, Swift case binding suffers from two weaknesses.
> Mixed external and internal let/var binding may introduce errors from uncommon edge cases.
> Real-world users may not consider the parallel construction between if case/guard case with switchstatements or naturally connect the two layouts.
>  <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#internal-case-binding>Internal Case Binding
> When pattern matching, it's common to bind a variable or constant. It's uncommon but legal to use a bound value as an argument. Adopting an "always explicit, always within the parentheses" rule adds consistency and safety to Swift. 
> Consider the following enumeration and values:
> // An enum with one, two, or three associated values
> enum Value<T> { case one(T), two(T, T), three(T, T, T) }
> // An example with two associated values
> let example2: Value<Character> = .two("a", "b")
> // A bound symbol
> let oldValue = "x"
> This code's goal is to conditionally bind newValue and pattern match the value stored in the oldValue symbol. The first example succeeds. The second example compiles and runs but does not match the coder's intent. Using an external letcreates a new oldValue shadow instead of pattern matching oldValue's stored value.
> // Safe
> if case .two(let newValue, oldValue) = example2 { 
>     ... 
> }
> // Syntactically legal but incorrect
> if case let .two(newValue, oldValue) = example2 { 
>     ... 
> }

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170301/77279ff6/attachment.html>

More information about the swift-evolution mailing list