3

as you might know, Ruby has a shorthand operator & which let's us easily convert a Symbol to a Proc. Example:

%w(a b c).map(&:upcase) #=> ["A", "B", "C"]

Would be equivalent to:

%w(a b c).map { |c| c.upcase } #=> ["A", "B", "C"]

And the explanation is that &:upcase is calling to_proc on :upcase to create a Proc object.

So, that pretty much explains what the operator does when it's used as the last argument of a method.

However, looks like it's not possible to use the operator anywhere outside the params of a method call:

:upcase.to_proc  => #<Proc:0x007febbc85ab38(&:upcase)>
&:upcase         => SyntaxError: syntax error, unexpected &

This would have been nice to use like this:

case number
when &:even?
  # ...
when &:prime?
  # ...
end

This works though:

case number
when :even?.to_proc
  # ...
when :prime?.to_proc
  # ...
end

In short, the unary operator & can only be used in the arguments of a method, for example in arr.map(&:upcase). What is the reason for that?

xuuso
  • 571
  • 5
  • 14
  • Possible duplicate of [what is the functionality of "&: " operator in ruby?](http://stackoverflow.com/questions/9429819/what-is-the-functionality-of-operator-in-ruby) – Wand Maker Jun 30 '16 at 18:24
  • What you think as method argument is indeed a block parameter of the method - `Symbol#to_proc` gets invoked when using `&` on symbol, and resultant proc is used as block parameter – Wand Maker Jun 30 '16 at 18:26
  • This is not a duplicate of that question. I'm not asking what's the functionality of the operator, the question itself even explains that roughly. I'm interested in knowing why this is only available in a certain context (i.e. method arguments). – xuuso Jul 01 '16 at 08:51

1 Answers1

9

The & unary prefix ampersand operator "unpacks" a Proc (or an object that can be converted to a Proc by sending it the to_proc message) into a block, passing it as if it had been passed as a block. Only message sends can have block arguments, ergo the & unary prefix ampersand operator is only allowed for the last argument in an argument list to a message send.

Likewise, the "dual" unary prefix ampersand operator "packs" a block into a Proc and is thus only allowed for the last parameter in a parameter list of either a block or a method definition.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • `:upcase` in `&:upcase` is a symbol - can you please explain your pharse - unpacks a `Proc` into a block - in this context. – Wand Maker Jun 30 '16 at 19:12
  • 1
    `&` is not a general shorthand for `to_proc`. Its use is specific to block arguments to method invocations. "unary ampersand" is not an operator outside of that context. – user229044 Jun 30 '16 at 19:30
  • 1
    @Wand, in `["oh", "my"].map(&:upcase)` `&` converts the method represented by the symbol `:upcase` to a proc *and* then calls that proc. – Cary Swoveland Jun 30 '16 at 19:43
  • 1
    @CarySwoveland: nope, it doesn't call the proc. The `map` calls the proc/block. – Sergio Tulentsev Jul 01 '16 at 09:05
  • @SergioTulentsev, a bitwise AND is not unary, it requires 2 operands. – xuuso Jul 01 '16 at 09:14
  • @Jörg, so the `&` operator is actually creating a block rather than a proc, using the `:to_proc` method to do so. That makes sense as the method is expecting a block and not a proc argument. Thanks for your answer :) – xuuso Jul 01 '16 at 09:17
  • @Jörg, I'm still trying to understand your last paragraph. What do you mean by "the dual unary prefix ampersand operator". – xuuso Jul 01 '16 at 09:18
  • @xuuso: oh right, it was a brain fart on my side. :) – Sergio Tulentsev Jul 01 '16 at 09:28
  • 1
    @xuuso: what Jörg meant is that the & operator is used in two opposite situations. One to wrap the block into a proc (`def foo(&block)`), the other is to unwrap the proc into a block (`bar(&block)`). Note that the first usage is in method definition, the other is in method invocation. – Sergio Tulentsev Jul 01 '16 at 09:30
  • @xuuso: splat and kwsplat operators possess the same dual behaviour. – Sergio Tulentsev Jul 01 '16 at 09:33