Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby's &method notation. what is going on?

Tags:

ruby

I came across the &method in a codebase and I have no idea what is going on. This happens in irb:

[12,3].map(&method(:to_s))
#=> ArgumentError: wrong number of arguments (given 1, expected 0)
["12","3"].map(&method(:Integer))
#=> [12, 3]

What is going on here?

I'm familiar with & being equal to to_proc, but I still can't connect the dots here.

like image 896
Jwan622 Avatar asked Jun 19 '26 08:06

Jwan622


2 Answers

In &method(:to_s), .to_s is taken from current context (top level object main). This version is already bound to receiver and does not accept further arguments. But an argument will be passed by .map (each element of the array), that's what it does.

Take a look at this step-by-step reconstruction of what's happening

to_s # => "main"
method(:to_s) # => #<Method: main.to_s>
method(:to_s).to_proc # => #<Proc:0x007ff73a27e1e0 (lambda)>
method(:to_s).to_proc.call(12) # => 
# ~> -:6:in `to_s': wrong number of arguments (given 1, expected 0) (ArgumentError)
# ~>    from -:6:in `<main>'

Now compare to what would happen if it was .map(&:to_s)

:to_s.to_proc.call(12) # => "12"

I came across the &method in a codebase

It can be useful in situations like this: apply a piece of logic to each element, but this logic comes from the current context, completely external to the elements. Take a look at this contrived example:

class Tweet
  attr_accessor :text

  def initialize(text)
    @text = text
  end

  def shortened_links
    find_links.map(&method(:shorten_link))
    # same as
    # find_links.map {|link| shorten_link(link) }
  end

  private

  def find_links
    # detect links in text
  end

  def shorten_link(url)
    # use bit.ly or whatever
  end
end

Here links is a collection of strings. They most certainly can't shorten themselves.

like image 113
Sergio Tulentsev Avatar answered Jun 20 '26 22:06

Sergio Tulentsev


Here is your code where & literal replaced by appropriate block:

[12,3].map { |v| to_s(v) }

["12","3"].map { |v| Integer(v) }

When it called in console, to_s and Integer are methods in Object class. (Technically Integer() is from Kernel, that is included in Object).

to_s definition has no arguments, that is why you get ArgumentError.

Integer method could accept 1 or 2 arguments.

like image 33
mikdiet Avatar answered Jun 20 '26 23:06

mikdiet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!