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


I am the author of aforementioned Qo, so I believe I can shed some light on a few things:

* The thinking behind Qo
* Reusable syntax features
* Making it feel like Ruby
* Shortcomings of current syntax
* Performance considerations
* Ideas moving forward

I will warn you that this may be a very long post, so I will attempt to break it into the above sections to make it easier to read. The last section will propose my ideas on syntax, and will effectively summarize the others.

You can find Qo here: https://github.com/baweaver/qo

I have tried to be detailed in my documentation, and will continue to expand upon it.

### The thinking behind Qo

Qo emerged as a result of the TC39 proposal for pattern matching as well as a conversation I had at Fog City Ruby about real pattern matching in Ruby, and made me ask a few questions:

* How can we do this using what Ruby already has?
* How can we make this look and feel like Ruby?
* How can we make the performance acceptable?

I wrote an article that explains, in more detail than I will here, the first point: https://medium.com/@baweaver/for-want-of-pattern-matching-in-ruby-the-creation-of-qo-c3b267109b25

### How can we use current Ruby syntax and make it feel like Ruby?

Ruby is an exceptionally powerful language, and jumping back to the basics we can find `===` and `to_proc`. Both are used throughout current language features with implicit syntax:

```ruby
# === is implied around case statements
case 'string'
when String then 1
else 2
end

# in grep
data.grep(String)

# and now in predicate methods
data.any?(String)
```

For `to_proc` it's a very similar story:

```ruby
data.map(&ProcObject)
```

Both are well understood in Ruby and utilize common features. Implementing classes that expose such methods allow them to utilize these same features for more powerful APIs.

`to_ary` was one of my next potentials for destructuring and right hand assignment emulation in pattern matching, but I have not yet found an acceptable solution to this.

Point being, by using existing language features we retain a very Ruby-like feel:

```ruby
case Person.new('', nil)
when Qo[age: :nil?] then 'No age provided!'
else 'nope'
end
```

Though this brings us to the next point: limitations of current syntax.

### Limitations of current syntax

One of the key items of pattern matching that I would very much enjoy is Right Hand Assignment, or the ability to inject local variables in the resulting branch of a match.

Case statements will not allow this, so something such as this is not possible as `when` will not be able to return a value:

```ruby
people.map { |person|
  case person
  when Qo.m(name: :*, age: 20..99) { |person| "#{person.name} is an adult that is #{person.age} years old" }
  else Qo.m(:*)
  end
}

```

This was the reason behind me introducing the concept of `match` with Qo:

```ruby
Qo.match(['Robert', 22],
  Qo.m(:*, 20..99) { |n, a| "#{n} is an adult that is #{a} years old" },
  Qo.m(:*)
)
# => "Robert is an adult that is 22 years old"

people.map(&Qo.match(
  Qo.m(:*, 20..99) { |n, a| "#{n} is an adult that is #{a} years old" },
  Qo.m(:*)
))
# => "Robert is an adult that is 22 years old"
```

It is not as elegant and Ruby-like as I would like, but it is more succinct than some of the other implementations based in more functional language paradigms using purely lambda composition.

As a result, it also ends up being faster which leads me to the next section:

### Performance Considerations

I try and be very honest about the performance concerns related to Qo, being that it is heavily dynamic in nature. Admittedly in ways it tries to be too clever, which I am attempting to reconcile for greater performance gains.

The performance results can be seen here: https://github.com/baweaver/qo/blob/master/performance_report.txt

All tests are specified in the Rakefile and compare Qo to a Vanilla variant of the same task. The performance was better than I had anticipated but still did not reach quite the speed that I had wanted it to.
I will continue to test against this and make improvements. It is likely that I can cut down this time substantially from where it currently is.

It is also the reason you may see certain `===` related tickets on speed improvement :)

I will endeavor to bring more comprehensive performance testing, especially around pattern matching as I have no results to this point.

### Ideas moving forward

I would be interested in a match syntax such as this:

```ruby
new_value = match value
  when %m(:_, 20..99) { |_, age| age + 1 }
  else { |object| ... }
end
```

The block syntax will make this a less magical option as it can explicitly capture details on what was matched. Keyword arguments could be used as well, but few know of the keyword arguments in blocks:

```ruby
json.map { |name: 'default_name', age: 42| Person.new(name, age) }
```

Such syntax exists most similarly in Scala, notably with Case Classes. Other similar implementations are present in the JS TC39 proposal and to a lesser extent in Haskell.

I have yet to think of a way to implement Left Hand Assignment like Javascript destructuring, and I do not have ideas on how to achieve such a thing beyond using `to_ary` for implicit destructuring.

### Conclusion

Thank you for your time in reading, and I apologize for my verbosity, but this is a feature I would very much like to see in Ruby Core some day.

I would be more than happy to provide alternative ideas on syntax in regards to this idea if you do not find the proposed ones satisfactory.

- baweaver

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

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