<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 &lt;<a href="mailto:peterkamb@gmail.com" class="">peterkamb@gmail.com</a>&gt; 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. &nbsp;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="">&nbsp;&gt; &gt; Only the *first* matching case is executed. Subsequent matching cases are not executed.</div><div class="">&nbsp;&gt; This is not unrelated to pattern matching, this is the expected behavior of every pattern matching algorithm.</div><div class="">&nbsp;</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="">&nbsp;</div><div class="">&nbsp;&gt; &gt; Allow filtering a single object through *multiple* cases of pattern matching, executing *all* cases that match.</div><div class="">&nbsp;&gt; 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. &nbsp;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. &nbsp;The goal is not to execute a find-all, the goal is to execute a find - exactly like a regular expression. &nbsp;</div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class="">&nbsp;&gt; 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. &nbsp;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="">&lt;<a href="mailto:devteam.codafi@gmail.com" target="_blank" class="">devteam.codafi@gmail.com</a>&gt;</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.&nbsp; Comments inline:<br class=""><br class=""><div id="m_8520758629169460191AppleMailSignature" class="">~Robert Widmann&nbsp;</div><div class=""><br class="">2017/11/17 15:06、Peter Kamb via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank" class="">swift-evolution@swift.org</a>&gt;<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:&nbsp;&nbsp;</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="">&nbsp;- 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.&nbsp; The intent is to compile a switch-case tree down to (conceptually) a (hopefully minimal) if-else tree.&nbsp; 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="">&nbsp;- `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.&nbsp; 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="">&nbsp; &nbsp; always executed if pattern matches</div><div class="">case patternTwo:</div><div class="">&nbsp; &nbsp; 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="">&nbsp; &nbsp; let rawValue: Int</div><div class="">&nbsp; &nbsp; static let italics = TextFlags(rawValue: 1 &lt;&lt; 1)</div><div class="">&nbsp; &nbsp; static let bold&nbsp; &nbsp; = TextFlags(rawValue: 1 &lt;&lt; 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="">&nbsp; &nbsp; print("italics")</div><div class="">case let x where x.contains(.bold):</div><div class="">&nbsp; &nbsp; print("bold")</div><div class="">default:</div><div class="">&nbsp; &nbsp; 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="">&nbsp; &nbsp; print("italics")</div><div class="">case let x where x.contains(.bold):</div><div class="">&nbsp; &nbsp; 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="">&nbsp; &nbsp; print("italics")</div><div class="">}</div><div class=""><br class=""></div><div class="">if case let x = textFlags, x.contains(.bold) {</div><div class="">&nbsp; &nbsp; 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="">&nbsp;- 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="">&nbsp;- 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.&nbsp; 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="">&nbsp;- 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="">&nbsp;- 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="">&nbsp;- 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="">&nbsp;- 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="">&nbsp;- 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="">&nbsp;match value { case pattern:</div><div class="">&nbsp; &nbsp; print("matched")</div><div class="">}</div><div class="">```</div><div class=""><br class=""></div><div class="">&nbsp;- 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="">&nbsp; &nbsp; print("italics")</div><div class="">case (1, let x) where x.contains(.bold):</div><div class="">&nbsp; &nbsp; 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="">&nbsp; &nbsp; var output = ""</div><div class="">&nbsp; &nbsp; match 0 {</div><div class="">&nbsp; &nbsp; case (i % 3): output += "Fizz"</div><div class="">&nbsp; &nbsp; case (i % 3): output += "Buzz"</div><div class="">&nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; output = String(i)</div><div class="">&nbsp; &nbsp; }</div><div class="">&nbsp; &nbsp;&nbsp;</div><div class="">&nbsp; &nbsp; 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="">&nbsp; var output = “”</div><div class="">&nbsp; output += (i % 3 == 0) ? “Fizz” : “”</div><div class="">&nbsp; <span style="background-color:rgba(255,255,255,0)" class="">output += (i % 5 == 0) ? “Buzz” : “”</span></div><div class="">&nbsp; 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>