Issue #14912 has been updated by ktsj (Kazuki Tsujimoto). Thanks for the feedback. > But I feel the deconstruct method of Struct in the sample code is tricky > because it duplicates values. > - Deconstruction seems fragile; For instance the following case statement matches, which is very counter-intuitive. It is trade-off with duck typing. Consider following case. ```ruby class MyA def deconstruct dummy = A[nil, nil] return dummy, my_x, my_y end end obj = MyA case obj in A(x, y) ... end ``` We can match the pattern even if `obj` is not an instance of `A` class. > I guess "& pattern", pat & pat & ..., may solve this problem. > ("pat1 & pat2 & ..." matches if all patterns (pat1, pat2, ...) matches.) > - There is `|` operator that is good. But why don't you have counterpart `&` operator? If `&` operator is also introduced, I think a user want to use parenthesis to control precedence of patterns. It conflicts with syntax of my proposal. > - Pinning operator is necessary. However the proposed syntax do not introduce an _operator_ rather it introduces naming convention into local variable naming. This is no good. We need a real operator for that purpose. Agreed. > - One-liner mode seems less needed at the moment. Is it necessary for the first version? We can add this later if a real-world use-case is found that such shorthand is convenient, rather than cryptic. Agreed. > - Some attendees do not like that arrays cannot be pattern matched as such. > > ```ruby > case [1, 2, [3, 4]] > in [a, b, [3, d]] # <- unable to do this > ... > end > ``` I can understand motivation of this request. > - Should `#deconstruct` be called over and over again to the same case target? Shouldn't that be cached? I think it should be cached. ---------------------------------------- Feature #14912: Introduce pattern matching syntax https://bugs.ruby-lang.org/issues/14912#change-73052 * 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: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>