Ruby block, procs and instance_eval
Asked Answered
U

5

16

I recently tried to do something akin to this:

a = "some string"
b = Proc.new{ upcase }
a.instance_eval b

Which gives the error:

TypeError: can't convert Proc into String

but this works:

def b(&block)
  "some string".instance_eval &block
end

b{ upcase }

A further look with this method:

def b(&block)
  "some string".instance_eval block
end

Yields the same Proc to String error.

So... my understanding of blocks is that they are just procs. But there's obviously something special about having this & ampersand...

Can someone explain this to me? Is it possible to convert a normal proc to be whatever it is that is special about this &block object?

edit

Just figured out my second question, prepend an & to the proc... that was easy, but WHAT is this really doing?

Unison answered 16/6, 2011 at 13:24 Comment(0)
G
24

All you have to do for your first example to work is this:

>> a.instance_eval &b #=> "SOME STRING"

The reason is that instance_eval needs either a string or a block and the ampersand provides the latter.

Grimaud answered 16/6, 2011 at 13:29 Comment(2)
it's funny how i started to piece through this problem as i was writing this question and asking others... and in the end realized i just needed to know the difference between a proc and a block. Thank you so much!!Unison
dead link providedProsaism
D
12

The difference is that a.instance_eval b is passing b as a regular argument to instance_eval, whereas a.instance_eval &b is passing it as a block. Those are two different things.

Consider this method call:

obj.foo(bar) do |x| 
  stuff(x) 
end

That invokes the method foo with one regular argument (bar) and one block argument (do |x| stuff(x) end). In the method definition, they're distinguished by prefixing & to the block parameter:

def foo(arg, &block)

And if you wish to pass a variable expression instead of a literal block, that is likewise accomplished by prefixing & to the expression (which should yield a Proc).

If you pass an argument with no &, it goes in the arg slot instead of the block slot. It doesn't matter that the argument happens to be an instance of Proc. The syntax dictates how it is passed and treated by the method.

Demote answered 17/6, 2011 at 2:36 Comment(1)
good concise answer, thanks! (sorry i had already accepted @Michaels as he directed me to the diff between a block and Proc)Unison
A
1

It's because instance_eval accepts a string to eval or a block. instance_eval(&block) is passing your block as a block to instance_eval.

Ardeha answered 16/6, 2011 at 13:27 Comment(1)
but what's the difference between a block and a proc?Unison
T
1

The crucial difference is that a Proc instance is an object whereas a block is not an object. The & is an operator that interchanges a block and a Proc instance mutually.

All arguments to a method must be an object. In addition to the arguments, a method can take a block. instance_eval is a method that takes either a String argument or a block. Passing a Proc object will satisfy neither case. If you attach & to a Proc object, that will be handeled as a block.

Tierza answered 16/6, 2011 at 13:42 Comment(0)
I
0

This will work:

a = "some string"
b = Proc.new{ upcase }
a.instance_eval &b

The instance_eval method can receive an block arguments. b is a Proc.

Ible answered 11/8, 2013 at 8:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.