[swift-evolution] [Discussion] Simplifying case syntax
Daniel Leping
daniel at crossroadlabs.xyz
Tue Feb 28 13:19:20 CST 2017
I like the ~> operator, but not := operator. It makes the language
inconsistent. Then we should allow using it without "if" just like this:
mylet := "hey there"
I think we should keep let as it was and avoid :=
P.S.: it also looks too much Pascal-ish :)
On Tue, 28 Feb 2017 at 21:02 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170228/243ae031/attachment.html>
More information about the swift-evolution
mailing list