Ruby 3.1 introduces pattern matching pin operator against expression
source link: https://blog.saeloun.com/2021/07/07/ruby-3-1-pattern-matching-pin-operator
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Ruby 3.1 introduces pattern matching pin operator against expression
Jul 7, 2021 , by Manmeet Singh 3 minute readPattern matching
was introduced in Ruby 2.7,
a feature commonly found in functional programming languages like Scala.
Unlike other programming languages,
pattern matching in Ruby uses the case
statement.
Instead of using the when
keyword,
the keyword in
is used.
Let us check the below example to see how pattern matching works in Ruby.
case [1, 2, 3]
in [Integer, *]
"matched"
else
"not matched"
end
#=> "matched"
data = { a: 1, b: 2, c: 3 }
case data
in { a: Integer, ** }
"matched"
else
"not matched"
end
#=> "matched"
We have passed a variable data
(a hash) and an expression [1, 2, 3]
to the case
statement.
The data
variable values and array indexes are matched with
the pattern of every in
clause.
Once a pattern has matched the code within that in
clause gets executed.
Besides structural checks, one of the features of pattern matching is the binding of the matched parts to local variables.
case [1, 2]
in Integer => a, Integer
"matched: #{a}"
else
"not matched"
end
#=> "matched: 1"
puts a
#=> 1
As seen in the above example,
the local variable a
was assigned the value 1
when an
expression matches to a pattern.
Due to the variable binding feature,
the existing local variable cannot be used straightforwardly as a sub-pattern.
For the below example,
the variable current_price
should not have changed.
But because of variable binding, the value got updated.
current_price = 15
case [1, 2]
in current_price, *rest
"matched. current_price was: #{current_price}"
else
"not matched. current_price was: #{current_price}"
end
# expected: "not matched. current_price was: 15"
# real: "matched. current_price was: 1" -- local variable updated
To avoid such issues, “variable pinning” operator (^) can be used, which will use the value for pattern matching.
current_price = 15
case [1, 2]
in ^current_price, *rest
"matched. current_price was: #{current_price}"
else
"not matched. current_price was: #{current_price}"
end
#=> "not matched. current_price was: 15"
Before
The pin operator worked fine in the case of variables, constants, and literals but failed when an expression or range is passed.
Let’s take an example where we have the following JSON data:
data = { "name": "Alice",
"age": 30,
"children": [
{
"name": "Bob",
"age": 6
},
{
"name": "Jilly",
"age": 4
}
]
}
We want to match all children of Alice in the range 2 to 4. Using pattern matching, if we try to pass the age parameter as a range it will fail with a syntax error.
case data
in name: "Alice", children: [*, { name: child_name, age: ^(1..3) }]
"matched: #{child_name}"
else
"not matched"
end
SyntaxError ((irb):61: syntax error, unexpected `end')
After
Ruby 3.1 introduces a pin operator against expressions to fix the above issue. It has been committed to the trunk and can be tested/played using Ruby 3.1.0-dev through RubyBuild.
We can now pass expressions and range using pin operator and, it should work fine without raising an error.
case "Do you like cats?"
in ^(/like/)
puts "Match"
end
#=> Match
case data
in name: "Alice", children: [*, { name: child_name, age: ^(1..3) }]
"matched: #{child_name}"
else
"not matched"
end
#=> matched: Jilly
Note:
Pattern matching is still an experimental feature and, a warning is displayed when it is used.
case [1, 2, 3]
in [Integer, *]
"matched"
else
"not matched"
end
#warning: Find pattern is experimental, and the behavior may change in future versions of Ruby!
#=> "matched"
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK