Fultie
Fultie

Reputation: 105

&: syntax for methods with arguments

I have a nested array:

a = [[1, "Aldous Huxley"],
     [3, "Sinclair Lewis"],
     [2, "Ernest Hemingway"],
     [4,"Anthony Burgess"]]

a.collect { |author| author.drop(1) }

outputs

[["Aldous Huxley"], ["Sinclair Lewis"], ["Ernest Hemingway"], ["Anthony Burgess"]]

while

a.collect(&:drop(1))

Gives me a sintax error.

SyntaxError: (irb):18: syntax error, unexpected '(', expecting ')'
a.collect(&:drop(1))
                  ^

Is it possible to define my original block expression in a &: syntax?

Upvotes: 0

Views: 101

Answers (2)

Simple Lime
Simple Lime

Reputation: 11090

The colon is part of a Symbol in Ruby, the & is the magic that makes that syntax work, by calling to_proc on the object and using that as a block. So, while you can't use a symbol like :drop(1), you can accomplish this without using a symbol in 2 ways:

1) Return a proc from a method and pass that in:

def drop(count)
  proc { |instance| instance.drop(count) }
end
a.collect(&drop(1))

2) Pass in an object that responds to to_proc:

class Drop
  def initialize(how_many = 1)
    @to_drop = how_many
  end

  def to_proc
    proc { |instance| instance.drop(@to_drop) } 
  end
end
drop = Drop.new(1)
a.collect(&drop) # or, more succinctly
a.collect(&Drop.new(1))

In a simple case like this, defining methods that return procs or objects that respond to to_proc is probably overkill and just passing a block to collect is better, but there could be some use cases where it's not.


Update: Thinking about this more, the Symbol class doesn't have a call method, but that doesn't mean it can't:

module SymbolCall
  def call(*args)
    proc { |instance| instance.public_send(self, *args) }
  end
end

class Symbol
  include SymbolCall
end

a.collect(&:drop.(1)) # or
a.collect(&:drop.call(1))

Just make sure you don't forget that it's actually a method call :drop.(1) not :drop(1).

Upvotes: 6

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

No, this is impossible.

& syntax is the syntactic sugar, that does literally the following:

  • converts the argument to Proc instance, calling to_proc on it;
  • uses the proc as a block, passing arguments from the enumerator to it.

(&:foo) works because Symbol.to_proc exists.

(&:foo(arg)) just can not be parsed, since : expects valid symbol chars afterwards, and opening parenthesis will raise a syntax error.

Upvotes: 6

Related Questions