Project

General

Profile

Feature #12115

Add Symbol#call to allow to_proc shorthand with arguments

Added by felixbuenemann (Felix Bünemann) over 1 year ago. Updated over 1 year ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:74011]

Description

I am a great fan of the Symbol#to_proc shorthand when mapping or reducing collections:

[1,2,16].map(&:to_s)
=> ["1", "2", "16"]
[1,2,16].reduce(&:*)
=> 32

I often wish it would be possible to pass an argument to the method when doing this, which currently requires a block and is more verbose:

[1,2,16].map { |n| n.to_s(16) }
=> ["1", "2", "10"]
# active_support example
{id: 1, parent_id: nil}.as_json.transform_keys { |k| k.camelize :lower }.to_json
=> '{"id":1,"parentId":null}'

It would be much shorter, if ruby allowed this:

[1,2,16].map(&:to_s.(16))
=> ["1", "2", "10"]
# active_support example
{id: 1, parent_id: nil}.as_json.transform_keys(&:camelize.(:lower)).to_json
=> '{"id":1,"parentId":null}'

This can be implemented easily, by adding the Symbol#call method:

class Symbol
  def call(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

Source: stackoverflow: Can you supply arguments to the map(&:method) syntax in Ruby?

I think this is a rather common use case, so I propose to add Symbol#call to the Ruby standard library.


Related issues

Related to Ruby trunk - Feature #4146: Improvement of Symbol and ProcRejected

History

#2 [ruby-core:74013] Updated by felixbuenemann (Felix Bünemann) over 1 year ago

Edited to remove **kwargs argument I added, which would require checking if the called method supports them.

#3 Updated by nobu (Nobuyoshi Nakada) over 1 year ago

#4 [ruby-core:74059] Updated by shelvacu (Shel vacu) over 1 year ago

I agree that there should be some syntax for doing this, but I don't think this is the proper way to do it.

Personally, the syntax is confusing to me. I would prefer something like:

[1,2,16].map(&:to_s(16))

This way, the way my brain parses it is that &: is the operator for turning a symbol into a block that calls the method on the argument. However, this would require changes in the parser instead of stdlib.

My other concern is that Symbol#call returning a proc feels wrong. It leads to code like this:

a = :to_s
a.call(16).call(15)

While such code may never be written even if this is implemented, I hope it conveys how odd it feels to have a method named "call" always return a proc which is then meant to be called, instead of calling anything.

Would the change from &(:meth_name_as_symbol) to special operator &: followed by method name and optionally arguments (ie. the syntax I used above) break any existing code?

#5 [ruby-core:74065] Updated by shevegen (Robert A. Heiler) over 1 year ago

I think there have been many other similar proposals. Nobu linked to other
discussions.

From what I have seen, I think the major problem is coming up with a nice
syntax proposal.

.map(&:foo)

is ok because it is short.

Adding implicit arguments to it is harder.

[1,2,16].map(&:to_s(16))

Is probably ok. But I am not sure if the parser is happy with it.

[1,2,16].map(&:to_s.(16))

Is not good IMHO, the . there is very confusing for me.

There is a slight alternative to .call() which is the []

I like [] a lot, but I think it looks a bit weird too
inside of ().

Perhaps we do not have a syntax that will be better if
we require arguments for &: ?

#6 [ruby-core:74066] Updated by felixbuenemann (Felix Bünemann) over 1 year ago

Although I don't understand the Japanese, the linked issue, with a similar syntax to what Shel vacu proposed above, was rejected by Matz. So probably not too much hope on getting this into core…

#7 [ruby-core:74074] Updated by nobu (Nobuyoshi Nakada) over 1 year ago

Yes, &:to_s(16) is exactly my (rejected) proposal.

#8 [ruby-core:74079] Updated by sawa (Tsuyoshi Sawada) over 1 year ago

For a similar proposal, please cf. #10394.

Also available in: Atom PDF