Issue #14709 has been updated by baweaver (Brandon Weaver).


It was mentioned to me on Reddit that I should detail exactly what cases of pattern matching would entail.

For the case of Qo, these include four primary categories:

* Array matches Array
* Array matches Object
* Hash matches Hash
* Hash matches Object

From there each category typically descends into the following chain:

1. Wildcards: `b == WILDCARD`
2. Strict equality: `a == b`
3. Case equality: `a === b`
4. Predicates: `a.public_send(b)`


These vary between Array and Hash style matches.

This will also be long as it explains the exact contract I've implemented, so as to be a possible inspiration for design level concerns of such a feature.

### Array matches Array

An Array matched against another Array in Qo will result in them being compared positionally on intersection.

In the most basic of cases, `[1,1]` matched against itself would compare the elements by their index, resulting in (using a hypothetical match method):

```ruby
match([1, 1], [1, 1]) # => true

# [1, 1]
# [1, 1]
```

My decision with Qo was to be left-side permissive on length, such that this will also return true:

```ruby
match([1, 1], [1, 1, 2, 3, 4]) #=> true

# [1, 1]
# [1, 1, 2, 3, 4]
```

Only the first two items would be compared.

For this, comparisons will be run as:

1. Wildcard matches: `b == :*`
2. Strict equality: `[1, 1] == [1, 1]`
3. Case equality: `match([Integer, 1..10], [1, 1])`
4. Predicate: `match([:odd?, :zero?], [1, 0])`

I would say that for performance concerns case 4 could be subsumed into case 3 by transferring the symbols to Procs instead of relying on `public_send`.

### Array matches Object

An Array matched against an Object will attempt to compare that Object to everything in the Array via:

1. Wildcard matches: `b == :*`
2. Case equality: `match([Integer, 1..10], 1)`
3. Predicate: `match([:odd?, :zero?], 1)`

In some cases this can be emulated with `compose` as suggested in another bug with procs, but `===` responding values will no longer have that flexibility.

Even if an `Array#to_proc` were to exist, composing against `===` responding values would fail.

This would be more difficult to optimize for performance

### Hash matches Hash

A Hash matched against another Hash would be via intersection of keys, and in the following order:

1. Key exists: `a.key?(b)`
2. Wildcard matches: `b == :*`
3. Recurse if value is a Hash
4. Case equality: `b[k] === a[k]`
5. Predicate: `a[k].public_send(b)`
6. Repeat with String coercion of key

Qo tries to be clever here, and starts to be more strict. It wants the key to exist or it counts a non-match even if a wildcard was provided to it.

The cleverness comes into play in that it tries the string version of the key as well, just in case. This is a performance bottleneck, but also a nice to have considering how often Rubyists get the two mixed up. Problem is it adds a good deal of overhead, and keyword arguments cannot take string keys with hashrockets.

### Hash matches Object

This starts to look a lot more like ActiveRecord syntax, except in that it applies to plain ruby objects using `public_send`. It applies in the following order:

1. Object responds to key
2. Wildcard matches
3. Case equality
4. Predicate

It is also strict about the presence of a key / method before continuing, even if it were to be a wildcard.

Essentially each key is a method to send to the object, and each value is what it must match against somehow:

```
match(Person('Foo', 42), name: :*, age: 40..50)
```

### On Docs

A majority of this is explained in more detail in the Qo docs, as well as practical examples I've tried myself in the wild.

If you have any other questions or concerns I would be happy to address them.


----------------------------------------
Feature #14709: Proper pattern matching
https://bugs.ruby-lang.org/issues/14709#change-71630

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
On RubyKaigi 2017, there was a [presentation](http://rubykaigi.org/2017/presentations/yotii23.html) of Yuki Torii about possible implementation of pattern matching.

The syntax proposed in presentation was:

```ruby
res = [:ng, 500]
case res
when %p([:ng, status])
  p status
end
```

The proposed syntax seem to feel pretty consistent, and the implementation (forked Ruby interpreter) was working at this moment.

As @ko1 was one of the contributors to the experiment, I don't suppose Ruby core team is not aware of the proposal, so I'd like to know what the status of it? Are there some plans for full-strength pattern matching in Ruby 3 (with proposed, or any other, syntax)?

PS: There are many existing gems with some kind "almost real" pattern matching (including recently emerged [Qo](https://github.com/baweaver/qo)), yet I believe that the _only_ useful pattern matching can be provided language core. Otherwise, two main goals can't be achieved:
* reasonable performance (as the pattern-matching is useful mostly in complicated algorithms, which tend to repeat matches thousands of times);
* unpacking of parts of the patterns into local variables.



-- 
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>