Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return statements inside procs, lambdas, and blocks

I am having a lot of trouble understanding how return works in blocks, procs, and lambdas.

For instance, in the following case, why does batman_ironman_proc work, while batman_yield throw an error?

def batman_ironman_proc
  victor = Proc.new { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

def batman_yield
    yield
    "Iron man will win!"
end

victor = Proc.new { return "Batman will win!" }

puts batman_ironman_proc 
#batman_yield(&victor) === This code throws an error.
like image 754
voltair Avatar asked Mar 22 '13 04:03

voltair


People also ask

What is the main difference between procs and lambdas?

In Ruby, a lambda is an object similar to a proc. Unlike a proc, a lambda requires a specific number of arguments passed to it, and it return s to its calling method rather than returning immediately. proc_demo = Proc.

What is the difference between block proc and lambda?

When using parameters prefixed with ampersands, passing a block to a method results in a proc in the method's context. Procs behave like blocks, but they can be stored in a variable. Lambdas are procs that behave like methods, meaning they enforce arity and return as methods instead of in their parent scope.

How do you return a block in Ruby?

You cannot do that in Ruby. The return keyword always returns from the method or lambda in the current context. In blocks, it will return from the method in which the closure was defined. It cannot be made to return from the calling method or lambda.

How is a block different from a proc?

Procs are objects, blocks are notA proc (notice the lowercase p) is an instance of the Proc class. This lets us call methods on it and assign it to variables. Procs can also return themselves. In contrast, a block is just part of the syntax of a method call.


2 Answers

As one answer in the linked question shows:

The return keyword always returns from the method or lambda in the current context. In blocks, it will return from the method in which the closure was defined. It cannot be made to return from the calling method or lambda.

Your first example was successful because you defined victor in the same function you wanted to return from, so a return was legal in that context. In your second example, victor was defined in the top-level. The effect of that return, then, would not be to return from batman_yield (the calling method), but [if it were valid] to return from the top-level itself (where the Proc was defined).

Clarification: while you can access the return value of a block (i.e. "The value of the last expression evaluated in the block is passed back to the method as the value of the yield" - as per your comment), you can't use the return keyword, for the reason stated above. Example:

def batman_yield
    value = yield
    return value
    "Iron man will win!"
end

victor = Proc.new { return "Batman will win!" }
victor2 = Proc.new { "Batman will win!" }

#batman_yield(&victor) === This code throws an error.
puts batman_yield(&victor2) # This code works fine.
like image 180
mgibsonbr Avatar answered Oct 21 '22 12:10

mgibsonbr


I am coming from a C background and the way I would explain it is when you call a function you set a return instruction number and a register which will store the returned value. In case of proc and block the return instruction is not set because they are inlined or called in the same scope but they can still give back a returned value because these are independent functionalities. So, without a return instruction being set proc/block are giving us a LocalJumpError while if we just want to give a value back that is fine.

like image 33
sethi Avatar answered Oct 21 '22 11:10

sethi