Feature #21857
openIntroduce to_proc pattern
Description
When object has some logics, we can use &object syntax with Enumerable or Array, etc.
users.any?(&:admin?)
There are some times when I want to write pattern matching similar to the syntax, but currently we either have to write proc or combine pin operator with to_proc, which is a bit verbose.
users in [*, ^(:admin?.to_proc), *]
users in [*, -> (user) { user.admin? }, *]
Therefore, I propose introducing a new pattern into pattern matching.
The pattern matches an object such that pattern.to_proc === object.
This allows us to write code with a familiarity close to the syntax we're accustomed to.
users in [*, &:admin?, *]
Updated by matheusrich (Matheus Richard) about 2 months ago
Could you add why one would prefer writing users in [*, &:admin?, *] over users.any?(&:admin?)?
Updated by mame (Yusuke Endoh) about 2 months ago
- Status changed from Open to Assigned
- Assignee set to ktsj (Kazuki Tsujimoto)
Updated by sanfrecce-osaka (Masatoshi Moritsuka) 14 days ago
Could you add why one would prefer writing
users in [*, &:admin?, *]overusers.any?(&:admin?)?
Pattern matching can be combined with As pattern to both check whether something matches and bind the matched value to a variable at the same time.
Array#any? would suffice for just checking, but that's the key difference.
In the description of this issue, I also had As pattern in mind as a motivation:
users in [*, &:admin? => admin_user, *]
You might wonder if Array#find would do the job, but there are cases where I want pattern matching to guarantee that a match will always be found:
# admin_user could be nil, and it's not clear that this code expects it to always be found
admin_user = users.find(&:admin?)
# If no match, NoMatchingPatternError is raised, making it clear that this code expects a match to always exist
users => [*, &:admin? => admin_user, *]
Here's an example from code I wrote at work recently:
if statuses.intersection(available_statuses) in ^(:present?.to_proc) => target_statuses
# any logics using target_statuses
else
# any logics
end
Without pattern matching, assigning inside an if condition requires parentheses:
if (target_statuses = statuses.intersection(available_statuses)) && target_statuses.present?
# any logics using target_statuses
else
# any logics
end