[swift-evolution] [Discussion] Simplifying case syntax

Xiaodi Wu xiaodi.wu at gmail.com
Wed Mar 1 08:57:15 CST 2017


The criteria for a source-breaking change in Swift 4 are as follows:

- The existing syntax/API being changed must be actively harmful.
- The new syntax/API must clearly be better and not conflict with existing
Swift syntax.
- There must be a reasonably automatable migration path for existing code.

On Wed, Mar 1, 2017 at 02:30 Goffredo Marocchi <panajev at gmail.com> wrote:

> I would think that removing confusion and simplifying + rationalising the
> syntax IS the definition of a breaking change justified :). I do not see
> how Erica's proposal is in any way contrary to the philosophy behind the
> language or is removing anything people really depend on now (in terms of
> expressive ability).
>
> This should be nowhere near as controversial as the removal of pre and
> post increments and C style for loops, quite a slam dunk actually.
>
> Sent from my iPhone
>
> On 28 Feb 2017, at 23:45, Xiaodi Wu via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> Agree. Design 1 seems like an improvement over the status quo. Barring
> unexpected downsides to this, it's then a question of whether the
> improvements are large enough to justify a breaking change.
>
>
> On Tue, Feb 28, 2017 at 3:57 PM, David Hart via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> I’m happy that someone is trying to fix this small wart in the language.
> I’ve always wanted something like Design 1. It makes sense because we are
> already used to the pattern matching operator and we finally fix the
> inconsistencies between if/guard and switch.
>
> On 28 Feb 2017, at 20: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
>
> 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 valuesenum Value<T> { case one(T), two(T, T), three(T, T, T) }
> // An example with two associated valueslet example2: Value<Character> = .two("a", "b")
> // A bound symbollet 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 {
>     ...
> }
>
> In-parenthesis binding avoids accidental shadowing. It eliminates this
> class of error by adding let and var key words to each use point. This
> creates longer call sites but enumerations rarely contain more than three
> or four associated items.
>
> Adopting point-of-use binding enhances clarity and readability. Both if
> case let and if case var (plus case varand case let) may look like single
> compound keywords rather than a combination of two distinct actions to
> developers unfamiliar with this syntax.
>
> <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#pattern-matching-with-conditional-binding>Pattern
> Matching with Conditional Binding
>
> Swift's guard case and if case align statement design with the switch statement,
> moving the matched value to the right of an equal sign.
>
> switch value {
>     case .enumeration(let embedded): ...
> }
> if case .enumeration(let embedded) = value
>
> The status quo for the = operator is iteratively built up in this fashion:
>
>    - = performs assignment
>    - let x = performs binding
>    - if let x = performs conditional binding on optionals
>    - if case .foo(let x) = performs conditional binding on enumerations
>    *and* applies pattern matching
>
> Using if case/guard case in the absense of conditional binding duplicates
> basic pattern matching with less obvious meaning. These two statements are
> functionally identical:
>
> if range ~= myValue { ... } // simplerif case range = myValue { ... } // confusing
>
> Issues with the current design include:
>
>    - guard case and if case look like standard non-conditional assignment
>    statements but they are *not* assignment statements. Using the
>    assignment operator violates the principle of least astonishment
>    <https://en.wikipedia.org/wiki/Principle_of_least_astonishment>.
>    - In switch, a case is followed by a colon, not an assignment operator.
>    - Swift *has* a pattern matching operator (~=) but does not use it
>    here.
>    - case syntax is wordy. The statement includes case, =, and optionally
>    let/var conditional binding. Design alternatives could streamline this
>    syntax, enhance clarity, and introduce a more concise format.
>
>
> <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#detailed-design>Detailed
> Design
>
> This proposal adopts point-of-use conditional binding and recommends one
> of the following designs. A successful design will replace the current
> syntax with a simpler grammar that prioritizes pattern matching and support
> conditional binding.
>
> <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#design-1-using-the-pattern-matching-operator>Design
> 1: Using the Pattern Matching Operator
>
> This design drops the case keyword and replaces = with ~=. The results
> look like this, showcasing a variety of letplacement, variable binding,
> and optional sugar alternatives.
>
> guard .success(let value) ~= result else { ... }guard .success(var value) ~= result else { ... }if .success(let value) ~= result { ... }if .success(var value) ~= result { ... }guard let x? ~= anOptional else { ... }if let x? ~= anOptional { ... }
>
> In this design:
>
>    - The case keyword is subsumed into the (existing) pattern matching
>    operator
>    - The statements adopt the existing if-let/if var and guard-let/guard
>    var syntax, including Optionalsyntactic sugar.
>
> if let x = anOptional { ... } // currentif case let x? = anOptional { ... } // would be removedif let x? ~= anOptional { ... } // proposed replacement for `if case`
>
> Pattern matching without conditional binding simplifies to a standalone
> Boolean condition clause. On adopting this syntax, the two identical range
> tests naturally unify to this single version:
>
> if range ~= myValue { ... } // beforeif case range = myValue { ... } // beforeif range ~= myValue { ... } // after
>
>
> <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#design-2-using-a-declare-and-assign-operator>Design
> 2: Using a Declare and Assign Operator
>
> This design introduces new := "declare and assign" operator. This
> operator eliminates the need for explicit let, although the keyword is
> allowed and most house style guides would recommend its use:
>
> guard .success(value) := result else { ... } guard .success(let value) := result else { ... }if .success(value) := result { ... }if .success(let value) := result { ... }guard value? := anOptional else { ... } // newly legal, although unnecessaryguard let value? := anOptional else { ... } // newly legal, although unnecessary
>
> Assignments to variables require the var keyword, enabling coders to
> clarify the distinct roles in mix-and-match pattern matching:
>
> guard .pair(value1, var value2) := result else { ... } // implied letguard .pair(let value1, var value2) := result else { ... } // explicit letif .success(var value) := result { ... } // variable assignmentguard var x? := anOptional else { ... } // variable assignmentguard var x := anOptional else { ... } // simpler variable assignmentguard var x = anOptional else { ... } // even simpler (current) variable assignmentguard x := anOptional else { ... } // new constant assignment
>
> Adopting this syntax provides more natural results for binding associated
> enumeration variables.
>
> <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#excluded-from-this-proposal>Excluded
> from this proposal
>
> This proposal does not address switch case or for case beyond internal
> binding requirements.
>
> <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#impact-on-existing-code>Impact
> on Existing Code
>
> This proposal is breaking and would require migration. External let or var would
> automatically be moved by fixits into use points. Current guard case and if
> case syntax would be migrated to the new design.
> <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#timeline>
> Timeline
>
> Although removing if case and guard case are breaking, this proposal
> should wait until Swift 4 Stage two to allow proper debate and
> consideration from the core team.
>
> <https://gist.github.com/erica/06dad9bbe1a70290fe6b89a64f73bc0c#alternatives-considered>Alternatives
> Considered
>
>    - Leaving the grammar as-is, albeit confusing
>    - Retaining case and replacing the equal sign with ~= (pattern
>    matching) or : (to match the switch statement).
>    - Adding matches or is as an alternative to the pattern matching
>    operator
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170301/5a292f52/attachment-0001.html>


More information about the swift-evolution mailing list