<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Nov 18, 2017, at 4:42 AM, Peter Kamb <<a href="mailto:peterkamb@gmail.com" class="">peterkamb@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class=""><div class="">Thanks for the review.</div><div class=""><br class=""></div><div class="">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.</div></div></div></blockquote><div><br class=""></div><div>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.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class=""> > > Only the *first* matching case is executed. Subsequent matching cases are not executed.</div><div class=""> > This is not unrelated to pattern matching, this is the expected behavior of every pattern matching algorithm.</div><div class=""> </div><div class="">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.</div><div class=""> </div><div class=""> > > Allow filtering a single object through *multiple* cases of pattern matching, executing *all* cases that match.</div><div class=""> > Again, this is not pattern matching.</div><div class=""><br class=""></div><div class="">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`.</div></div></div></blockquote><div><br class=""></div><div>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. </div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class=""> > This is not boilerplate!</div><div class=""><br class=""></div><div class="">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:`).</div></div></div></blockquote><div><br class=""></div><div>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.</div><div><br class=""></div><div>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).</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div></div></div></blockquote><blockquote type="cite" class=""><div class=""><div class="gmail_extra"><div class="gmail_quote">On Sat, Nov 18, 2017 at 12:19 AM, Robert Widmann <span dir="ltr" class=""><<a href="mailto:devteam.codafi@gmail.com" target="_blank" class="">devteam.codafi@gmail.com</a>></span> wrote:<br class=""><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto" class=""><div class=""><span class=""></span></div><div class="">Having spent a lot of time with ‘switch’, I don’t understand any of the motivations or corresponding justifications for this idea. Comments inline:<br class=""><br class=""><div id="m_8520758629169460191AppleMailSignature" class="">~Robert Widmann </div><div class=""><br class="">2017/11/17 15:06、Peter Kamb via swift-evolution <<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>><wbr class="">のメール:<br class=""><br class=""></div><span class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="">## Title</div><div class=""><br class=""></div><div class=""><div class="">Add `match` statement as `switch`-like syntax alternative to `if case` pattern matching</div></div><div class=""><br class=""></div><div class="">## Summary:</div><div class=""><br class=""></div><div class="">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`.</div><div class=""><br class=""></div><div class="">However, the `switch` statement has several unique behaviors unrelated to pattern matching. Namely: </div></div></div></blockquote><div class=""><br class=""></div><div class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class=""> - Only the *first* matching case is executed. Subsequent matching cases are not executed.</div></div></div></blockquote><div class=""><br class=""></div></span><div class="">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.</div><span class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""> - `default:` case is required, even for expressions where a default case does not make sense.</div></div></div></blockquote><div class=""><br class=""></div></span><div class="">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.</div><div class=""><div class="h5"><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class="">These behaviors prevent `switch` from being used as a generic match-patterns-against-a-<wbr class="">single-expression statement.</div><div class=""><br class=""></div><div class="">Swift should contain an equally-good pattern-matching statement that does not limit itself single-branch switching.</div><div class=""><br class=""></div><div class="">## Pitch:</div><div class=""><br class=""></div><div class="">Add a `match` statement with the same elegant syntax as the `switch` statement, but without any of the "branch switching" baggage.</div><div class=""><br class=""></div><div class="">```</div><div class="">match someValue {</div><div class="">case patternOne:</div><div class=""> always executed if pattern matches</div><div class="">case patternTwo:</div><div class=""> always executed if pattern matches</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class="">The match statement would allow a single value to be filtered through *multiple* cases of pattern-matching evaluation.</div><div class=""><br class=""></div><div class="">## Example:</div><div class=""><br class=""></div><div class="">```</div><div class="">struct TextFlags: OptionSet {</div><div class=""> let rawValue: Int</div><div class=""> static let italics = TextFlags(rawValue: 1 << 1)</div><div class=""> static let bold = TextFlags(rawValue: 1 << 2)</div><div class="">}</div><div class=""><br class=""></div><div class="">let textFlags: TextFlags = [.italics, .bold]</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">// SWITCH STATEMENT</div><div class="">switch textFlags {</div><div class="">case let x where x.contains(.italics):</div><div class=""> print("italics")</div><div class="">case let x where x.contains(.bold):</div><div class=""> print("bold")</div><div class="">default:</div><div class=""> print("forced to include a default case")</div><div class="">}</div><div class="">// prints "italics"</div><div class="">// Does NOT print "bold", despite .bold being set.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">// MATCH STATEMENT</div><div class="">match textFlags {</div><div class="">case let x where x.contains(.italics):</div><div class=""> print("italics")</div><div class="">case let x where x.contains(.bold):</div><div class=""> print("bold")</div><div class="">}</div><div class="">// prints "italics"</div><div class="">// prints "bold"</div><div class="">```</div></div></div></blockquote><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class="">## Enum vs. OptionSet</div><div class=""><br class=""></div><div class="">The basic difference between `switch` and `match` is the same conceptual difference between `Emum` and an `OptionSet` bitmask.</div><div class=""><br class=""></div><div class="">`switch` is essentially designed for enums: switching to a single logical branch based on the single distinct case represented by the enum.</div><div class=""><br class=""></div><div class="">`match` would be designed for OptionSet bitmasks and similar constructs. Executing behavior for *any and all* of the following cases and patterns that match.</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">## Existing Alternatives</div><div class=""><br class=""></div><div class="">`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.</div><div class=""><br class=""></div><div class="">Example using a string of independent `if case` statements:</div><div class=""><br class=""></div><div class="">```</div><div class="">if case let x = textFlags, x.contains(.italics) {</div><div class=""> print("italics")</div><div class="">}</div><div class=""><br class=""></div><div class="">if case let x = textFlags, x.contains(.bold) {</div><div class=""> print("bold")</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class="">## `match` statement benefits:</div><div class=""><br class=""></div><div class=""> - Allow filtering a single object through *multiple* cases of pattern matching, executing *all* cases that match.</div></div></div></blockquote><div class=""><br class=""></div></div></div><div class="">Again, this is not pattern matching.</div><span class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class=""> - A syntax that exactly aligns with the familiar, succinct, elegant, and understandable `switch` syntax.</div></div></div></blockquote><div class=""><br class=""></div></span><div class="">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.</div><span class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class="">- The keyword "match" highlights that pattern matching will occur. Would be even better than `switch` for initial introductions to pattern-matching.</div><div class=""><br class=""></div><div class=""> - 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) {`</div></div></div></blockquote><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class=""> - 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."</div></div></div></blockquote><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class=""> - A single `match controlExpression` at the top rather than `controlExpression` being repeated (and possibly changed) in every single `if case` statement.</div><div class=""><br class=""></div><div class=""> - 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.</div><div class=""><br class=""></div><div class=""> - 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.</div><div class=""><br class=""></div><div class="">```</div><div class=""> match value { case pattern:</div><div class=""> print("matched")</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class=""> - 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.)</div></div></div></blockquote><div class=""><br class=""></div></span><div class="">This is not boilerplate!</div><span class=""><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class="">## Prototype</div><div class=""><br class=""></div><div class="">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:</div><div class=""><br class=""></div><div class="">```</div><div class="">match: for eachCase in 0...1 {</div><div class="">switch (eachCase, textFlags) {</div><div class="">case (0, let x) where x.contains(.italics):</div><div class=""> print("italics")</div><div class="">case (1, let x) where x.contains(.bold):</div><div class=""> print("bold")</div><div class="">default: break }</div><div class="">}</div><div class=""><br class=""></div><div class="">// prints "italics"</div><div class="">// prints "bold"</div><div class="">```</div><div class=""><br class=""></div><div class="">## Notes / Discussion:</div><div class=""><br class=""></div><div class="">- 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!</div><div class=""><br class=""></div><div class="">- 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.</div><div class=""><br class=""></div><div class="">- FizzBuzz using proposed Swift `match` statement:</div><div class=""><br class=""></div><div class="">```</div><div class="">for i in 1...100 {</div><div class=""> var output = ""</div><div class=""> match 0 {</div><div class=""> case (i % 3): output += "Fizz"</div><div class=""> case (i % 3): output += "Buzz"</div><div class=""> default: output = String(i)</div><div class=""> }</div><div class=""> </div><div class=""> print(output)</div><div class="">}</div><div class=""><br class=""></div><div class="">// `15` prints "FizzBuzz"</div><div class="">```</div></div></div></blockquote><div class=""><br class=""></div>for i in 1...100 {</span></div><div class=""> var output = “”</div><div class=""> output += (i % 3 == 0) ? “Fizz” : “”</div><div class=""> <span style="background-color:rgba(255,255,255,0)" class="">output += (i % 5 == 0) ? “Buzz” : “”</span></div><div class=""> print(output.isEmpty ? “\(i)” : output)</div><div class="">}</div><div class=""><br class=""></div><div class="">If control flow should branch multiple times, <b class="">why not write just write it that way!</b></div><div class=""><br class=""><blockquote type="cite" class=""><div class=""><span class="">______________________________<wbr class="">_________________</span><br class=""><span class="">swift-evolution mailing list</span><br class=""><span class=""><a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a></span><br class=""><span class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank" class="">https://lists.swift.org/<wbr class="">mailman/listinfo/swift-<wbr class="">evolution</a></span><br class=""></div></blockquote></div></div></blockquote></div><br class=""></div>
</div></blockquote></div><br class=""></body></html>