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) 4 months ago
Could you add why one would prefer writing users in [*, &:admin?, *] over users.any?(&:admin?)?
Updated by mame (Yusuke Endoh) 4 months ago
- Status changed from Open to Assigned
- Assignee set to ktsj (Kazuki Tsujimoto)
Updated by sanfrecce-osaka (Masatoshi Moritsuka) 2 months 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
Updated by ktsj (Kazuki Tsujimoto) 11 days ago
I understand the motivation, but I'm still not fully convinced that this particular use case is enough to justify introducing dedicated syntax.
There are similar requests as well, such as wanting to match based on respond_to?. It might be more convincing if the proposal included a syntax that could cover those kinds of cases too.