We are Koichi Sasada (ko1) and Yusuke Endoh (mame) from Cookpad Inc. tech team. Cookpad sponsors us to work full time developing the Ruby interpreter (MRI: Matz Ruby Implementation).

We released a Japanese article “Ruby 2.7 NEWS explained by Ruby Professionals” when Ruby 2.7 was released on 25th Dec. 2019. This is an English translation of the article with help from Miles Woodroffe.

“NEWS” is a text file that lists all new features and changes of the Ruby interpreter. Compared to a few years ago, we are making an effort to make the file easier to read, for example, by adding many examples. Some of the code in the article is quoted from the NEWS file. In this article, in addition to a description of the new features and changes, we will explain the background of “why and how the changes have been introduced” as much as possible to hopefully make it easier to understand.

The Ruby 2.7 release has a lot of changes leading to Ruby 3, which is scheduled for release next year, in 2020. Also, as always, many useful new features and performance improvements have been introduced. We hope you will enjoy them.

Language Changes

Changes to the grammar, semantics, etc. of the Ruby programming language.

1. Pattern Matching

Pattern matching has been experimentally added [Feature #14912]. The feature is to check and deconstruct a data structure nicely.

What is a pattern match?

At first glance, it looks like the case / when statement that you are familiar with, but the new syntax is case / in . The semantics are also similar to case / when ; it will find a pattern that matches the value of {a: 0, b: 1, c: 2} , from the top to the bottom.

in {a: 0, x: 1} means “the key a exists and its value is 0” and “the key x exists and its value is 1”.

{a: 0, b: 1, c: 2} satisfies the first condition, but does not the second condition because there is no x . So the pattern does not match the value.

Then, it tries the next pattern in {a: 0, b: var} . This means “the key a exists and its value is 0” and “the key b exists and its value is anything, and substitute it for the variable var ”.

The value satisfies both conditions, so the pattern matches the value, assigns 1 to var , and executes the corresponding clause, i.e., p var in this case.

If no pattern matches, a NoMatchingPatternError exception is thrown. Note that this is different from case / when syntax.

As a concrete use case, you can use it to check that the JSON data has the expected structure, and then take out the necessary data at once. You don’t have to use #dig anymore.

It will take a long time to explain the full details of pattern matching. For more on this, see the material by Kazuki Tsujimoto who designed and implemented Ruby’s pattern matching (may be a little old unfortunately).

What was difficult in introducing pattern matching?

Here is the background of the change.

Pattern matching is a feature often used in statically typed functional programming languages. It has been anticipated for Ruby for a long time: Many people tried to emulate it in Ruby, proposed it for Ruby, or prototyped it with Ruby:

However, it was difficult to propose a suitable language-builtin syntax. This is because Ruby’s syntax is too flexible and has little room for expansion. It is almost impossible to introduce a new keyword because of backwards compatibility. In addition, usually, a pattern syntax for pattern matching is similar to the construction syntax: [x, y, z] for an array and {a: x, b: y} for a hash.

Kazuki Tsujimoto settled this situation. He suggested reusing the keyword in . Ruby has a iteration syntax for ... in (that is rarely used these days), and for this syntax in was already a keyword, so there is no need to introduce a new one. It might not be the best keyword to express a pattern matching, but the case / in syntax is reasonably intuitive. Using this idea, the introduction of pattern matching became a reality.

Kazuki made the prototype proposal of grammar and semantics for pattern matching in 2018, triggered the discussion, implemented in 2019, committed at the time of RubyKaigi 2019, and repeated the experiments and discussions for more than half a year. Finally it is released in 2.7.

However, it is still experimental, and if you use it, you will get the following warning.

$ ./miniruby -e 'case 1; in 1; end'

-e: 1: warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!

In the future, I think that it will be stabilized through finer improvements from using it more widely. Putting it into production code may be a risk, but I’d like you to give it a try and give us feedback.

(Credit: mame)