From: kazuki@... Date: 2018-07-15T23:03:36+00:00 Subject: [ruby-core:87953] [Ruby trunk Feature#14912] Introduce pattern matching syntax Issue #14912 has been updated by ktsj (Kazuki Tsujimoto). > Is the above exclusive for "in" in case, or can it be combined with "when"? The former is right, but I don't have any strong opinion about it. The reason why I select the former is that "in" is superset of "when". ---------------------------------------- Feature #14912: Introduce pattern matching syntax https://bugs.ruby-lang.org/issues/14912#change-72960 * Author: ktsj (Kazuki Tsujimoto) * Status: Open * Priority: Normal * Assignee: * Target version: ---------------------------------------- I propose new pattern matching syntax. # Pattern syntax Here's a summary of pattern syntax. ``` # case version case expr in pat [if|unless cond] ... in pat [if|unless cond] ... else ... end pat: var # Variable pattern. It matches any value, and binds the variable name to that value. | literal # Value pattern. The pattern matches an object such that pattern === object. | Constant # Ditto. | var_ # Ditto. It is equivalent to pin operator in Elixir. | (pat, ..., *var, pat, ..., id:, id: pat, ..., **var) # Deconstructing pattern. See below for more details. | pat(pat, ...) # Ditto. Syntactic sugar of (pat, pat, ...). | pat, ... # Ditto. You can omit the parenthesis (top-level only). | pat | pat | ... # Alternative pattern. The pattern matches if any of pats match. | pat => var # As pattern. Bind the variable to the value if pat match. # one-liner version $(pat, ...) = expr # Deconstructing pattern. ``` The patterns are run in sequence until the first one that matches. If no pattern matches and no else clause, NoMatchingPatternError exception is raised. ## Deconstructing pattern This is similar to Extractor in Scala. The patten matches if: * An object have #deconstruct method * Return value of #deconstruct method must be Array or Hash, and it matches sub patterns of this ``` class Array alias deconstruct itself end case [1, 2, 3, d: 4, e: 5, f: 6] in a, *b, c, d:, e: Integer | Float => i, **f p a #=> 1 p b #=> [2] p c #=> 3 p d #=> 4 p i #=> 5 p f #=> {f: 6} e #=> NameError end ``` This pattern can be used as one-liner version like destructuring assignment. ``` class Hash alias deconstruct itself end $(x:, y: (_, z)) = {x: 0, y: [1, 2]} p x #=> 0 p z #=> 2 ``` # Sample code ``` class Struct def deconstruct; [self] + values; end end A = Struct.new(:a, :b) case A[0, 1] in (A, 1, 1) :not_match in A(x, 1) # Syntactic sugar of above p x #=> 0 end ``` ``` require 'json' $(x:, y: (_, z)) = JSON.parse('{"x": 0, "y": [1, 2]}', symbolize_names: true) p x #=> 0 p z #=> 2 ``` # Implementation * https://github.com/k-tsj/ruby/tree/pm2.7-prototype * Test code: https://github.com/k-tsj/ruby/blob/pm2.7-prototype/test_syntax.rb # Design policy * Keep compatibility * Don't define new reserved words * 0 conflict in parse.y. It passes test/test-all * Be Ruby-ish * Powerful Array, Hash support * Encourage duck typing style * etc * Optimize syntax for major use case * You can see several real use cases of pattern matching at following links :) * https://github.com/k-tsj/power_assert/blob/8e9e0399a032936e3e3f3c1f06e0d038565f8044/lib/power_assert.rb#L106 * https://github.com/k-tsj/pattern-match/network/dependents -- https://bugs.ruby-lang.org/ Unsubscribe: