Project

General

Profile

Actions

Feature #14912

closed

Introduce pattern matching syntax

Added by ktsj (Kazuki Tsujimoto) over 3 years ago. Updated 10 months ago.

Status:
Closed
Priority:
Normal
Target version:
-
[ruby-core:87945]

Description

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

Design policy


Related issues

Related to Ruby master - Feature #14709: Proper pattern matchingClosedActions
Related to Ruby master - Feature #15865: `<expr> in <pattern>` expressionClosedmatz (Yukihiro Matsumoto)Actions
Related to Ruby master - Feature #15918: Pattern matching for SetOpenktsj (Kazuki Tsujimoto)Actions
Related to Ruby master - Feature #15881: Optimize deconstruct in pattern matchingOpenktsj (Kazuki Tsujimoto)Actions
Related to Ruby master - Feature #15824: respond_to pattern for pattern matchOpenActions
Related to Ruby master - Feature #17260: Promote pattern matching to official featureClosedActions
Has duplicate Ruby master - Feature #15814: Capturing variable in case-when branchesClosedActions
Actions

Also available in: Atom PDF