Symbol#to_proc with custom methods
class Integer
def times_three
return self * 3
end
end
Now, because times_three is now a method of the Integer class, you can do symbol to proc...
[1, 2, 3].map(&:times_three)
If you want to access a method that isn't part of the object's class but acts on an object, you need to pass the object as an argument to the method...
def times_three(x)
x * 3
end
[1, 2, 3].map{|i| times_three(i) }
the symbol to proc
needs to use the object as a receiver.
[1, 2, 3].map(&:some_action)
is equivalent to
[1, 2, 3].map{|i| i.some_action}
What does to_proc method mean in Ruby?
Some methods take a block, and this pattern frequently appears for a block:
{|x| x.foo}
and people would like to write that in a more concise way. In order to do that they use a combination of: a symbol, the method Symbol#to_proc
, implicit class casting, and &
operator. If you put &
in front of a Proc
instance in the argument position, that will be interpreted as a block. If you combine something other than a Proc
instance with &
, then implicit class casting will try to convert that to a Proc
instance using to_proc
method defined on that object if there is any. In case of a Symbol
instance, to_proc
works in this way:
:foo.to_proc # => ->x{x.foo}
For example, suppose you write:
bar(&:foo)
The &
operator is combined with :foo
, which is not a Proc
instance, so implicit class cast applies Symbol#to_proc
to it, which gives ->x{x.foo}
. The &
now applies to this and is interpreted as a block, which gives:
bar{|x| x.foo}
Using Symbol#to_proc with multiple procs
If you are looking for a good architectural solution then you need to implement FIFO stack for that.
Or you can do it like you mentioned above:
Object.tap(&:do_work).tap(&:do_more_work)
Chaining & to_proc on symbol
If you're only doing:
%i[a b c].map { |e| e.to_s.upcase }
then just use the block and get on with more important things. If you're really doing a chain of Enumerable calls and find the blocks too visually noisy:
%i[a b c].map { |e| e.to_s.upcase }.some_chain_of_enumerable_calls...
then you could toss your logic into a lambda to help clean up the appearance:
to_s_upcase = lambda { |e| e.to_s.upcase }
%i[a b c].map(&to_s_upcase).some_chain_of_enumerable_calls...
or throw it in a method and say:
%i[a b c].map(&method(:to_s_upcase)).some_chain_of_enumerable_calls...
Either way, you're giving your little bit of logic a name (which is pretty much all &:symbol
is doing for you) to make the code more readable and easier to understand. In the specific case of to_s.upcase
, this is all a bit pointless but these approaches are quite useful when the block gets bigger.
Implementing &:method with custom methods
It seems you are expected to extend Array
class with new method accumulate
which will accumulate the results of invoking a given proc
on each element of the array.
One implementation can be like this:
class Array
def accumulate(&block)
self.collect { |i| block.yield(i) }
end
end
p result = %w(hello world).accumulate(&:upcase) # Prints ["HELLO", "WORLD"]
p result = %w(hello world).accumulate { |b| b.upcase } # Prints ["HELLO", "WORLD"]
Please note that %w(HELLO WORLD)
is same as array ["HELLO", "WORLD"]
There is a very good explanation of what is the use of &
operator in this article - please read the section on The Unary &
Symbol#to_proc shorthand with the stabby lambda syntax
Apparently, it is not supported.
I think it has something to do with the fact that proc
and lambda
are actually methods, and not keywords. They support the same features that we usually associate with each
and the other methods from the Enumerable
module. However, ->
is a special language construct which is parsed separately.
I can't think of a reason why something like -> &:method
shouldn't be possible, but as of now the syntax of the Ruby language simply doesn't allow it.
Implement to_s(2) with String#to_proc in Ruby
When you run some_method(&some_obj)
, Ruby first call the some_obj.to_proc
to get a proc, then it "converts" that proc to a block and passes that block to some_method
. So how the arguments go into the proc depends on how some_method
passes arguments to the block.
For example, as you defined String#to_proc
, which returns a proc{|arg| ...}
(a proc with one argument), and calls [...].map(&'to_s 2')
, Ruby interprets it as
[...].map(&('to_s 2'.to_proc))
which is
[...].map(&proc{|arg| ... })
and finally
[...].map {|arg| ... }
In Ruby, how do you write a simple method that can be used with &:symbol?
The problem is that Symbol#to_proc
does not create a proc that calls add_message
method correctly.
# `yield` will pass its arguments to proc
>> :add_message.to_proc.call('passed to proc')
# => ArgumentError
This calls 'passed to proc'.add_message
, because our method is defined in Object
it works when called on String
, however it is missing the required argument.
The solution is to make a proc that can accept the same arguments as add_message
method and pass them along to that method. We can use Object#method
that returns Method
object that implements its own to_proc
and has the same arity as the method.
>> method(:add_message).to_proc.arity
=> 1
>> method(:add_message).to_proc.call('passed to proc')
from add_message passed to proc
>> foo(&method(:add_message))
before
from add_message passed to proc
after
Why does Ruby only allow to refer to methods by symbols when calling to_proc on them?
Given the following valid Ruby example:
"to_i".to_sym.to_proc.call("1")
To call #to_proc
on a Symbol creates a proc that receive one parameter: the object that should receive an invocation of the method named by the symbol. Something like this:
->(object) {
object.send(:to_i)
}
What the &
do is to pass the block returned by #to_proc
as a proper invocation block to the method being called. When used with Enumerable#map
, the final result is that the passed
block will be invoked for each element on the collection and receive
the current iteration element being passed as parameter to the block.
So the ["1", "2"].map(&:to_i)
syntax is a shortcut to something like
this:
block = ->(el) {
el.send(:to_i)
}
["1", "2"].map &block
About your examples:
When you try to call &to_i
in your example, Ruby will try to invoke
a variable or method named to_i
. Since it doesn't exists in the
scope (it is a method of each String
in the Array
, not in the "global" scope), it'll generate an error.
About the other example: "to_i".to_sym.to_proc
will transform the:to_i
into a proc that when invoked with one parameter, will try to
call the method named after the symbol (:to_i
) against the target
parameter. This means that you could use the &
to transform the proc
into a "invocation block":
["1", "2"].map(&"to_i".to_sym.to_proc)
How to define a method to_proc in Array so I can use it in a particular situations
The standard Symbol#to_proc
is, more or less, a e.send(self)
call and you just want to send
an array full of symbols to each element so just say exactly that with something like this:
class Array
def to_proc
proc { |e| self.map { |m| e.send(m) } }
end
end
I'd probably not patch Array for this, I'd just use a local lambda if I wanted something easier to read:
brand_and_color = lambda { |e| [:brand, :color].map { |s| e.send(s) } }
array_of_cars.map(&brand_and_color)
Related Topics
Rails 3 Order by Count on Has_Many :Through
How to Store an Instance Variable Across Multiple Actions in a Controller
How to Convert a Ruby String with Brackets to an Array
Bootstrap Datepicker Default Value Simple_Form_For
How to Extract a Single Character (As a String) from a Larger String in Ruby
Why Does Ruby Builder::Xmlmarkup Add Inspect Tag to Xml
How to Use Reference Images in SASS When Using Rails 3.1
Encryption-Decryption in Rails
Install Cocoapods Failed on MAC
Why Does Rails 4.2 + Responders Keeps Telling Me to Add Responders to the Gemfile
How to Store Nil User's Goal in a Session
Can "Gem Install" Be Configured to Install Executables Outside /Usr/Bin/ by Default
How to Get Out of a "Hung" State in Irb
How to Make a Class Whose Constructor Looks Like the Constructor of a Built-In Class