Home > front end >  To which level returns a return inside a Proc Object in Ruby?
To which level returns a return inside a Proc Object in Ruby?

Time:11-21

As I understood return inside a Proc terminates the current method. So in the following example I would expect to see: a1 > b1 > proc > a2. But actually it never reaches a2, why?

def a
  puts "a1"
  l = Proc.new {puts "proc"; return}
  b l
  puts "a2"
end

def b x
  puts "b1"
  x.call
  puts "b2"
end

a 

CodePudding user response:

As a general rule, return always returns from the closest lexically enclosing method definition expression.

In this case, the closest lexically enclosing method definition expression is def a, therefore, return returns from a.

It does not actually matter that the return is inside a block in this case. The general rule is, well, general, so it applies regardless of where the return appears.

If we look more specifically at blocks, though, we can see that it still makes sense: in blocks, local variables are captured lexically, self is captured lexically, so it makes sense that return also behaves lexically. It is a general property of blocks that if you want to understand what is going on in a block, you only need to look lexically outwards.

And if we get even more specific, first going from the general rule to blocks, and now from blocks to Procs, the behavior still makes sense: a Proc is essentially a reified block, so it makes sense for a Proc to behave like a block.

There are some exceptions, though, to the general rule, and one important one are lambdas. Talking about lambdas in Ruby is always a little bit weird because lambdas are Procs but they behave differently from Procs. IMO, lambdas should have a separate class alongside Procs. Since lambdas are Procs, it makes it weird to talk about the differences between lambdas and Procs which are not lambdas (which don't have a standardized name and thus are confusingly also called Procs).

The behavior of a lambda differs from the behavior of a non-lambda Proc in two ways, one of which is relevant to your question:

  • Parameter binding in non-lambda Procs has the same semantics as parameter binding in blocks, whereas parameter binding in lambdas has the same semantics as parameter binding in message sends / method invocations.
  • In non-lambda Procs, return returns from the closest lexically enclosing method definition expression, just like in blocks, whereas in lambdas, return returns from the lambda itself, just like return in methods.

So, in both of these aspects, non-lambda Procs behave like blocks and lambdas behave like methods. I memorize it like this: "Proc" rhymes with "block" and both "lambda" and "method" are Greek.

As you probably know, there are some methods which also alter the behavior of blocks that are passed to them. E.g. instance_eval and instance_exec change the value of self, and define_method actually does change the behavior of return.

But since you didn't ask about blocks in general, and also didn't ask about lambdas specifically, and there are no reflective methods in your question, the general rules still applies to non-lambda Procs like the one shown in your question: return returns from the closest lexically enclosing method definition expression.

  • Related