[swift-evolution] [Idea] [Pitch] Add `match` statement as `switch`-like syntax alternative to `if case` pattern matching
Peter Kamb
peterkamb at gmail.com
Sat Nov 18 13:30:14 CST 2017
> alternative to `fallthrough` that would continue matching cases
Yes, that would be interesting to look into. Do you have any references or
remember what the proposed keyword was called? Do any other languages have
this feature?
On Sat, Nov 18, 2017 at 10:53 AM Kevin Nattinger <swift at nattinger.net>
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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20171118/b394ca46/attachment.html>
More information about the swift-evolution
mailing list