Search code examples
rubyproc

Passing a proc as a block to a method


When I try to pass an arbitrary block to each_with_object method, it raises an error:

%w(foo bar baz).each_with_object([]) &->(i,m) { m << i.upcase }
# => NoMethodError: undefined method `&' for #<Enumerator: ["foo", "bar", "baz"]:each_with_object([])>

When I try to pass a block to inject method, it also raise an error:

%w(foo bar baz).inject('') &->(m,i) { m + i.upcase }
# => NoMethodError: undefined method `' for "foo":String

But it works if I don't pass an initial value:

%w(foo bar baz).split.inject &->(m,i) { m + i.upcase }

And it also works when I pass a block to each method.

%w(foo bar baz).each &->(i) { puts i.upcase }
# FOO
# BAR
# BAZ

Can anyone explain this behaviour? How can I pass arbitrary blocks to the first two examples?


Solution

  • The & argument prefix turns an argument into the method's block (by calling to_proc on it). Since it's an argument, it must go inside the parens if you are using them, i.e.

    %w(foo bar baz).each_with_object([], &->(i,m) { m << i.upcase })
    

    At the moment, ruby is interpreting the & as the binary operator, and trying to do:

    (%w(foo bar baz).each_with_object([])  & (->(i,m) { m << i.upcase }))
    

    Hence the error. I'm not sure why you're not just doing:

    %w(foo bar baz).each_with_object([]) { |m,i| m << i.upcase }