[swift-evolution] [Idea] [Pitch] Add `match` statement as `switch`-like syntax alternative to `if case` pattern matching

Benjamin G benjamin.garrigues at gmail.com
Sat Nov 18 16:25:54 CST 2017


I think because it's not immediately obvious with multiple if statement,
that they all try to compare the same expression to different patterns.

match exp {
case 1
case 2
}
vs
if case 1 = exp
if case 2 = anotherexp


On Sat, Nov 18, 2017 at 10:43 PM, Xiaodi Wu via swift-evolution <
swift-evolution at swift.org> wrote:

> On Sat, Nov 18, 2017 at 3:12 PM, Peter Kamb <peterkamb at gmail.com> wrote:
>
>> A high bar for new syntax is fair and expected, and by posting I was
>> hoping to maybe find an alternative in the comments here.
>>
>> But AFAIK there's currently no ability in Swift to:
>>
>> "Evaluate a *single* control expression against all of these patterns,
>> and execute any and all cases that match"
>>
>> Multiple `if-case` statements, each re-stating the control expression,
>> are ok.
>>
>> But that's definitely not as clear or concise as a switch-like construct
>> with the single control expression at the top. Or perhaps some other
>> alternative such as the mentioned `continue` or somehow enumerating a set
>> of cases.
>>
>
> You're simply restating your proposed new syntax as the thing that's
> missing. But what is the use case that motivates this construct? In what
> way are multiple if-case statements "not as clear"?
>
> On Sat, Nov 18, 2017 at 11:18 AM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>
>>> Robert is quite right--I'm not sure what we're designing for here.
>>> There's a very high bar for introducing new syntax and a distaste for the
>>> existing syntax is not a motivating use case.
>>>
>>>
>>> On Sat, Nov 18, 2017 at 12:53 PM, Kevin Nattinger via swift-evolution <
>>> swift-evolution at swift.org> wrote:
>>>
>>>> There have been earlier suggestions for an alternative to `fallthrough`
>>>> that would continue matching cases; I think that is much more likely to get
>>>> support than a whole new construct with only a subtle difference from an
>>>> existing one—would that be an acceptable alternative to you?
>>>>
>>>> > On Nov 17, 2017, at 12:06 PM, Peter Kamb via swift-evolution <
>>>> swift-evolution at swift.org> wrote:
>>>> >
>>>> > ## Title
>>>> >
>>>> > Add `match` statement as `switch`-like syntax alternative to `if
>>>> case` pattern matching
>>>> >
>>>> > ## Summary:
>>>> >
>>>> > The syntax of the `switch` statement is familiar, succinct, elegant,
>>>> and understandable. Swift pattern-matching tutorials use `switch`
>>>> statements almost exclusively, with small sections at the end for
>>>> alternatives such as `if case`.
>>>> >
>>>> > However, the `switch` statement has several unique behaviors
>>>> unrelated to pattern matching. Namely:
>>>> >
>>>> >  - Only the *first* matching case is executed. Subsequent matching
>>>> cases are not executed.
>>>> >  - `default:` case is required, even for expressions where a default
>>>> case does not make sense.
>>>> >
>>>> > These behaviors prevent `switch` from being used as a generic
>>>> match-patterns-against-a-single-expression statement.
>>>> >
>>>> > Swift should contain an equally-good pattern-matching statement that
>>>> does not limit itself single-branch switching.
>>>> >
>>>> > ## Pitch:
>>>> >
>>>> > Add a `match` statement with the same elegant syntax as the `switch`
>>>> statement, but without any of the "branch switching" baggage.
>>>> >
>>>> > ```
>>>> > match someValue {
>>>> > case patternOne:
>>>> >     always executed if pattern matches
>>>> > case patternTwo:
>>>> >     always executed if pattern matches
>>>> > }
>>>> > ```
>>>> >
>>>> > The match statement would allow a single value to be filtered through
>>>> *multiple* cases of pattern-matching evaluation.
>>>> >
>>>> > ## Example:
>>>> >
>>>> > ```
>>>> > struct TextFlags: OptionSet {
>>>> >     let rawValue: Int
>>>> >     static let italics = TextFlags(rawValue: 1 << 1)
>>>> >     static let bold    = TextFlags(rawValue: 1 << 2)
>>>> > }
>>>> >
>>>> > let textFlags: TextFlags = [.italics, .bold]
>>>> >
>>>> >
>>>> >
>>>> > // SWITCH STATEMENT
>>>> > switch textFlags {
>>>> > case let x where x.contains(.italics):
>>>> >     print("italics")
>>>> > case let x where x.contains(.bold):
>>>> >     print("bold")
>>>> > default:
>>>> >     print("forced to include a default case")
>>>> > }
>>>> > // prints "italics"
>>>> > // Does NOT print "bold", despite .bold being set.
>>>> >
>>>> >
>>>> >
>>>> > // MATCH STATEMENT
>>>> > match textFlags {
>>>> > case let x where x.contains(.italics):
>>>> >     print("italics")
>>>> > case let x where x.contains(.bold):
>>>> >     print("bold")
>>>> > }
>>>> > // prints "italics"
>>>> > // prints "bold"
>>>> > ```
>>>> >
>>>> > ## Enum vs. OptionSet
>>>> >
>>>> > The basic difference between `switch` and `match` is the same
>>>> conceptual difference between `Emum` and an `OptionSet` bitmask.
>>>> >
>>>> > `switch` is essentially designed for enums: switching to a single
>>>> logical branch based on the single distinct case represented by the enum.
>>>> >
>>>> > `match` would be designed for OptionSet bitmasks and similar
>>>> constructs. Executing behavior for *any and all* of the following cases and
>>>> patterns that match.
>>>> >
>>>> > The programmer would choose between `switch` or `match` based on the
>>>> goal of the pattern matching. For example, pattern matching a String.
>>>> `switch` would be appropriate for evaluating a String that represents the
>>>> rawValue of an enum. But `match` would be more appropriate for evaluating a
>>>> single input String against multiple unrelated-to-each-other regexes.
>>>> >
>>>> > ## Existing Alternatives
>>>> >
>>>> > `switch` cannot be used to match multiple cases. There are several
>>>> ways "test a value against multiple patterns, executing behavior for each
>>>> pattern that matches", but none are as elegant and understandable as the
>>>> switch statement syntax.
>>>> >
>>>> > Example using a string of independent `if case` statements:
>>>> >
>>>> > ```
>>>> > if case let x = textFlags, x.contains(.italics) {
>>>> >     print("italics")
>>>> > }
>>>> >
>>>> > if case let x = textFlags, x.contains(.bold) {
>>>> >     print("bold")
>>>> > }
>>>> > ```
>>>> >
>>>> > ## `match` statement benefits:
>>>> >
>>>> >  - Allow filtering a single object through *multiple* cases of
>>>> pattern matching, executing *all* cases that match.
>>>> >
>>>> >  - A syntax that exactly aligns with the familiar, succinct, elegant,
>>>> and understandable `switch` syntax.
>>>> >
>>>> > - The keyword "match" highlights that pattern matching will occur.
>>>> Would be even better than `switch` for initial introductions to
>>>> pattern-matching.
>>>> >
>>>> >  - No need to convert between the strangely slightly different syntax
>>>> of `switch` vs. `if case`, such as `case let x where x.contains(.italics):`
>>>> to `if case let x = textFlags, x.contains(.italics) {`
>>>> >
>>>> >  - Bring the "Expression Pattern" to non-branch-switching contexts.
>>>> Currently: "An expression pattern represents the value of an expression.
>>>> Expression patterns appear only in switch statement case labels."
>>>> >
>>>> >  - A single `match controlExpression` at the top rather than
>>>> `controlExpression` being repeated (and possibly changed) in every single
>>>> `if case` statement.
>>>> >
>>>> >  - Duplicated `controlExpression` is an opportunity for bugs such as
>>>> typos or changes to the expression being evaluated in a *single* `if case`
>>>> from the set, rather than all cases.
>>>> >
>>>> >  - Reduces to a pretty elegant single-case. This one-liner is an easy
>>>> "just delete whitespace" conversion from standard multi-line switch/match
>>>> syntax, whereas `if case` is not.
>>>> >
>>>> > ```
>>>> >  match value { case pattern:
>>>> >     print("matched")
>>>> > }
>>>> > ```
>>>> >
>>>> >  - Eliminate the boilerplate `default: break` case line for
>>>> non-exhaustible expressions. Pretty much any non-Enum type being evaluated
>>>> is non-exhaustible. (This is not the *main* goal of this proposal.)
>>>> >
>>>> > ## Prototype
>>>> >
>>>> > A prototype `match` statement can be created in Swift by wrapping a
>>>> `switch` statement in a loop and constructing each case to match only on a
>>>> given iteration of the loop:
>>>> >
>>>> > ```
>>>> > match: for eachCase in 0...1 {
>>>> > switch (eachCase, textFlags) {
>>>> > case (0, let x) where x.contains(.italics):
>>>> >     print("italics")
>>>> > case (1, let x) where x.contains(.bold):
>>>> >     print("bold")
>>>> > default: break }
>>>> > }
>>>> >
>>>> > // prints "italics"
>>>> > // prints "bold"
>>>> > ```
>>>> >
>>>> > ## Notes / Discussion:
>>>> >
>>>> > - Other Languages - I've been unable to find a switch-syntax
>>>> non-"switching" pattern-match operator in any other language. If you know
>>>> of any, please post!
>>>> >
>>>> > - Should `match` allow a `default:` case? It would be easy enough to
>>>> add one that functioned like switch's default case: run if *no other* cases
>>>> were executed. But, conceptually, should a "match any of these patterns"
>>>> statement have an else/default clause? I think it should, unless there are
>>>> any strong opinions.
>>>> >
>>>> > - FizzBuzz using proposed Swift `match` statement:
>>>> >
>>>> > ```
>>>> > for i in 1...100 {
>>>> >     var output = ""
>>>> >     match 0 {
>>>> >     case (i % 3): output += "Fizz"
>>>> >     case (i % 3): output += "Buzz"
>>>> >     default:      output = String(i)
>>>> >     }
>>>> >
>>>> >     print(output)
>>>> > }
>>>> >
>>>> > // `15` prints "FizzBuzz"
>>>> > ```
>>>> > _______________________________________________
>>>> > 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/20171118/1675af7b/attachment.html>


More information about the swift-evolution mailing list