From: mame@... Date: 2019-12-23T00:21:43+00:00 Subject: [ruby-core:96412] [Ruby master Feature#14912] Introduce pattern matching syntax Issue #14912 has been updated by mame (Yusuke Endoh). docx (Lukas Dolezal) wrote: > I wonder what do you think about only allowing curly brackets for hash pattern, and only square brackets for array pattern? The rationale of the current design I've heard from @ktsj is to fit the pattern to the style of its constructor. ``` Point = Struct.new(:x, :y, :z, keyword_init: true) pnt = Point[x: 1, y: 2, z: 3] pnt in Point[x: a, y: b, z: c] p [a, b, c] #=> [1, 2, 3] ``` `Point[x: 1, y: 2, z: 3]` corresponds elegantly to `Point[x: a, y: b, z: c]`. We cannot write `Point{x: 1, y: 2, z: 3}`. But personally, I agree with curly brackets for hash pattern. It is simpler and more intuitive when we focus on the pattern language. ---------------------------------------- Feature #14912: Introduce pattern matching syntax https://bugs.ruby-lang.org/issues/14912#change-83337 * Author: ktsj (Kazuki Tsujimoto) * Status: Assigned * Priority: Normal * Assignee: ktsj (Kazuki Tsujimoto) * Target version: 2.7 ---------------------------------------- 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: