I understand your point about multiple cases + the halting problem when applied to the implementation details of switch and pattern matching.
I think where we're missing is that what I'm envisioning would be syntactic sugar that would be de-sugared into a a line of single-case `if-case` statements. It would have nothing to do with matching multiple cases via the same mechanism as `switch`. Motivation for the change vs. a line of if-case statements is a valid question of course :) On Sat, Nov 18, 2017 at 10:48 AM, Robert Widmann <devteam.cod...@gmail.com> wrote: > > On Nov 18, 2017, at 4:42 AM, Peter Kamb <peterk...@gmail.com> wrote: > > 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. > > > That much I understand, but there’s no motivation as to why this kind of > change needs to occur over a line of if-case statements. There isn’t > anything in the pitch that demonstrates friction outside of a personal > distaste for the syntax. > > > > > 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`. > > > It is not pattern matching because it violates minimality - and we have a > warning today when you write switches of overlapping patterns. Consider > the semantics of pattern matching in the context of a tree - but it just so > happens the nodes of this tree are your patterns and the branches, actual > branches where control flow splits along the case-matrix. The goal is not > to execute a find-all, the goal is to execute a find - exactly like a > regular expression. > > > > 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:`). > > > You misunderstand me. This is the same problem as the one I brought up > before: You are subject to the halting problem in the general case. The > catch-all clause isn’t just noise, it’s a fundamental necessity to > guarantee sound semantics for this particular class of switch statements > that, according to a fundamental barrier in computer science, cannot be > checked for exhaustiveness. > > On top of that, we cannot allow you to write an uncovered switch statement > full of expression patterns because you would most-assuredly not handle all > possible cases (suppose I extend your OptionSet with a new bit-pattern in a > different module - your code will miscompile). > > > 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