Project

General

Profile

Actions

Feature #21857

open

Introduce to_proc pattern

Feature #21857: Introduce to_proc pattern

Added by sanfrecce-osaka (Masatoshi Moritsuka) 2 months ago. Updated 14 days ago.

Status:
Assigned
Target version:
-
[ruby-dev:<unknown>]

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 Actions #1

Could you add why one would prefer writing users in [*, &:admin?, *] over users.any?(&:admin?)?

Updated by mame (Yusuke Endoh) about 2 months ago Actions #2

  • Status changed from Open to Assigned
  • Assignee set to ktsj (Kazuki Tsujimoto)

Updated by sanfrecce-osaka (Masatoshi Moritsuka) 14 days ago Actions #3

Could you add why one would prefer writing users in [*, &:admin?, *] over users.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
Actions

Also available in: PDF Atom