Thanks for the review. Motivation is essentially to provide a low-friction way to write `if case / if case / if case` statements that are all matching on the same expression, much in the same way that `switch` offers a way to write `if / else if / else` statements against the same expression.
> > Only the *first* matching case is executed. Subsequent matching cases are not executed. > This is not unrelated to pattern matching, this is the expected behavior of every pattern matching algorithm. By that I meant that a string of `if case / if case / if case` statements, each individually matching against the same expression, would match and execute all 3 cases. A `switch` using the same 3 cases would execute only the first matching case. I took the "only first case" behavior to be a property of the *switch*, not of "pattern matching" in general? But perhaps I'm using the wrong terminology. > > Allow filtering a single object through *multiple* cases of pattern matching, executing *all* cases that match. > Again, this is not pattern matching. A switch can have multiple cases that "would" match and execute, if you were to comment out the successful case above them. Why would a hypothetical statement that executed *all* matching cases, rather than just the first case, not be pattern matching? It wouldn't be a `switch`, for sure, but it seems like it would be just as much "pattern matching" as an `if-case`. > This is not boilerplate! An `if case / if case` pair would not have the same concept of a shared `else/default` case used by a `switch` or `if/else`, which is why I said it would not be needed. `if-case` does not require an else/default. (Perhaps boilerplate was the wrong word, and I definitely didn't mean to suggest that switches should no longer require `default:`). > A syntax that duplicates the existing if-case construct Right, it would duplicate `if-case` just as `switch` duplicates `if-else`. Cheers, thanks. Peter On Sat, Nov 18, 2017 at 12:19 AM, Robert Widmann <devteam.cod...@gmail.com> wrote: > Having spent a lot of time with ‘switch’, I don’t understand any of the > motivations or corresponding justifications for this idea. Comments inline: > > ~Robert Widmann > > 2017/11/17 15:06、Peter Kamb via swift-evolution <swift-evolution@swift.org > >のメール: > > ## 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. > > > This is not unrelated to pattern matching, this is the expected behavior > of every pattern matching algorithm. The intent is to compile a > switch-case tree down to (conceptually) a (hopefully minimal) if-else > tree. You may be thinking of the behavior of switch in C and C-likes which > is most certainly not pattern matching and includes behavior Swift has > explicitly chosen to avoid like implicit fallthroughs. > > - `default:` case is required, even for expressions where a default case > does not make sense. > > > Expression patterns may look to be covered “at first glance”, but the > analysis required to prove that is equivalent to solving the halting > problem in the general case. Further, your proposed idea has absolutely > nothing to do with this. > > > 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. > > > Again, this is not pattern matching. > > > - A syntax that exactly aligns with the familiar, succinct, elegant, and > understandable `switch` syntax. > > > A syntax that duplicates the existing if-case construct which, I should > note, takes the same number of lines and roughly the same number of columns > to express as your match. Even less, considering you unwrap in the if-case > to exaggerate the example but not in the match. > > > - 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.) > > > This is not boilerplate! > > > ## 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" > ``` > > > for i in 1...100 { > var output = “” > output += (i % 3 == 0) ? “Fizz” : “” > output += (i % 5 == 0) ? “Buzz” : “” > print(output.isEmpty ? “\(i)” : output) > } > > If control flow should branch multiple times, *why not write just write > it that way!* > > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution > >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution