I am proposing runtime structural pattern matching to Crystal, akin to Ruby 3’s =>
and in
operators. Ideally, such a construct would let us write code like below:
# Before
ary = [1, 2, 3, 4, 5, 6, 7]
if ary.size >= 2
a, b = ary
rest = ary[2..]
else
raise "not enough elements"
end
# After
[1, 2, 3, 4, 5, 6, 7] ~> [a, b, *rest]
# Before
if ary.size == 3
first, x, y = ary
end
if first.is_a?(Circle) && x.is_a?(Int32) && y.is_a?(Int32)
first.move_to(x, y)
end
# After
if ary ~>? {first = Circle, x = Int32, y = Int32}
first.move_to(x, y)
end
# Before
anniversary = events.find do |ev|
ev.has_key?("start") && ev["start"].in?("2020-01-01".."2020-12-31") &&
ev.has_key?("id") && ev["id"] =~ /^ANNIV.*/
end["start"]
events.select! &.[]?("start").== anniversary
# After
events ~> {*_, {"start" => anniversary = "2020-01-01".."2020-12-31", "id" => /^ANNIV.*/}, *_}
events.select! &.~>?({"start" => ^anniversary})
This library contains a proof-of-concept implementation in the macro language that is good enough for toying with and defining the matching semantics. These pattern matching operators will be type-safe; the reference macros leverage flow typing as much as possible to achieve this.
The actual proposal is here. This is a first draft and I’d like to gather some feedback here before submitting an actual RFC issue.