From: kazuki@... Date: 2019-04-19T05:28:24+00:00 Subject: [ruby-core:92327] [Ruby trunk Feature#14912] Introduce pattern matching syntax Issue #14912 has been updated by ktsj (Kazuki Tsujimoto). Target version set to 2.7 Assignee set to ktsj (Kazuki Tsujimoto) Status changed from Closed to Assigned The specification is still under discussion, so I reopend the issue. Current specifation summary: ~~~ case obj 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 | pat | ... # Alternative pattern. The pattern matches if any of pats match. | pat => var # As pattern. Bind the variable to the value if pat match. | Constant(pat, ..., *var, pat, ...) # Array pattern. See below for more details. | Constant[pat, ..., *var, pat, ...] # Ditto. | [pat, ..., *var, pat, ...] # Ditto (Same as `BasicObject(pat, ...)` ). You can omit brackets (top-level only). | Constant(id:, id: pat, "id": pat, ..., **var) # Hash pattern. See below for more details. | Constant[id:, id: pat, "id": pat, ..., **var] # Ditto. | {id:, id: pat, "id": pat, ..., **var} # Ditto (Same as `BasicObject(id:, ...)` ). You can omit braces (top-level only). An array pattern matches if: * Constant === object returns true * The object has a #deconstruct method that returns Array * The result of applying the nested pattern to object.deconstruct is true A hash pattern matches if: * Constant === object returns true The object has a #deconstruct_keys method that returns Hash. The result of applying the nested pattern to object.deconstruct_keys(keys) is true ~~~ For more details, please see https://speakerdeck.com/k_tsj/pattern-matching-new-feature-in-ruby-2-dot-7. ---------------------------------------- Feature #14912: Introduce pattern matching syntax https://bugs.ruby-lang.org/issues/14912#change-77669 * 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: