Ruby's & has two opposite meanings, right?

The & here is not a bit operator but a proc-related thing. It may be obvious to those who know it, but I will write it down because it is easy for me to understand (and there was no other article that wrote this way).

Speaking of & in the first place

A notation for converting a block into a Proc object. In the simplest example

def test(&fun)
    fun.call
end

test { p 1 } #=> 1

This is displayed as 1. -Block {p 1} is passed to fun -This fun is converted to a Proc object by &. -Therefore, fun will accept calls using call, which is one of Proc's methods. That's the calculation.

By the way, it's off the subject, but in Ruby ・ "Yield is an abbreviation that calls the Proc generated by the & argument." -"Pass to yield & argument Proc can be omitted in the method definition line" Since it is stipulated that the above is

def test
    yield
end

test { p 1 } #=> 1

But it's OK. Shinpuru!

Then what's next?

def test(&fun)
    fun.call
end

proc = Proc.new { p 1 }
test &proc #=> 1

This is also displayed as 1. Here first I "(This part is wrong) & is a magic of conversion to Proc, so even if you add & to Proc's one, it's still Proc, and I wonder if the Proc object is passed in both & proc and & fun." I thought.

If the above is correct, it should be okay to remove one &. Proc should have been passed all the time. When I actually try it ...

def test(fun)
    fun.call
end

proc = Proc.new { p 1 }
test &proc
#=> wrong number of arguments(given 0, expected 1)

that? It's an error. What happened? Let's check the & proc class as a test.

proc = Proc.new { p 1 }
p proc.class #=> Proc
# p (&proc).class #=> syntax Error

Apparently proc and & proc are different. & proc doesn't seem to accept the .class itself. That should be it. In fact, & proc is not a __ object __. Ruby official documentation

If you pass a Proc object to a method with a block with `&', Acts like a call block.

It was written properly. And in Ruby, blocks aren't objects (in other words, I think Proc came from the desire to treat blocks as objects).

Thus, we can see that & has two meanings __. -Conversion from block to Proc object -Conversion from Proc object to block It's the exact opposite.

Hmm ... It seemed strange to be honest, but when I get used to it, does this seem to fit better?

Digression

The code that caused the error above works fine if you pass it in the Proc state without using & in the first place.

def test(fun)
    fun.call
end

proc = Proc.new { p 1 }
test proc #=> 1

It was pretty neat. If the argument does not use &, multiple Proc can be passed without any problem (in the case of formal argument with &, only one is decided per method).

def test(one, two)
    one.call
    two.call
end

proc1 = Proc.new { p 1 }
proc2 = Proc.new { p 2 }

test proc1, proc2
#=> 1
#   2

In this way, it seems that the specification works well as a language without defining non-Proc blocks.

However, after all I want to pass blocks directly to maps etc. like Ruby, and the yield notation mentioned above is hard to throw away. Then, & is important for readability, so I thought that I would probably have a long-term relationship with this guy. As a knack for dealing with it, the meaning of & changes depending on the context, so in the program (I think this can be said for all types and classes), "Is this a proc or a block now?" The current idea is that it is important to keep track of it.

reference

It was very helpful. Thank you very much. How to use Ruby block / proc / lambda The one who controls Proc controls Ruby (a lie)

Recommended Posts

Ruby's & has two opposite meanings, right?
java.util has two ArrayList classes