Why does explicit return matter in Proc?

def foo f = Proc.new { return "return from foo from inside proc" } f.call # control leaves foo here return "return from foo" end def bar b = Proc.new { "return from bar from inside proc" } b.call # control leaves bar here return "return from bar" end puts foo # prints "return from foo from inside proc" puts bar # prints "return from bar" 

I thought the return keyword is optional in Ruby and that you always return whether you request it or not. Given this, I find it surprising that foo and bar have different output, determined by the fact that foo contains an explicit return in Proc f .

Does anyone know why this is so?

+40
ruby return
Sep 16 '09 at 22:03
source share
3 answers

Ruby has three constructs:

  • The block is not an object and is created { ... } or do ... end .
  • Proc is a Proc object created by Proc.new or Proc .
  • lambda is a Proc created by lambda (or Proc in Ruby 1.8).

Ruby has three keywords that return from something:

  • return terminates the method or lambda in which it resides.
  • next completes the block, proc or lambda in which it resides.
  • break completes a method that yields to a block or is called by proc or the lambda in which it resides.

In lambdas, return behaves like next for any reason. next and break are called as they are, because they are most often used with methods such as each , where block completion will resume the iteration using the next element of the collection, and completion each will cause break to exit the loop.




If you use return inside the definition of foo , you will return from foo , even if it is inside a block or proc. To return from a block, you can use the next keyword.
 def foo f = Proc.new { next "return from foo from inside proc" } f.call # control leaves foo here return "return from foo" end puts foo # prints "return from foo" 
+61
Sep 16 '09 at 22:13
source share

This is semantics for Proc s; this is not necessarily semantics for all blocks. I agree that is a bit confusing. There is additional flexibility (and perhaps partly the reason Ruby does not have a specification other than its implementation).

The behavior is defined in the implementation of Proc . Lambda behave differently, so if you want your return not to get out of the placement method, use lambdas . Or omit the return keyword from your Proc .

An in-depth study of closing Rubys here . This is a fantastic story.

So:

 def foo f = Proc.new { p2 = Proc.new { return "inner proc"}; p2.call return "proc" } f.call return "foo" end def foo2 result = Proc.new{"proc"}.call "foo2 (proc result is: #{result})" end def bar l = lambda { return "lambda" } result = l.call return "bar (lambda result is: #{result})" end puts foo # inner proc puts foo2 # foo (proc result is: proc) puts bar # bar (lambda result is: lambda) 
+12
Sep 16 '09 at 23:28
source share

Think of it this way: Proc.new just create a block of code that is part of the calling function. proc / lambda creates an anonymous function with special bindings. A few code examples will help:

 def foo f = Proc.new { return "return from foo from inside Proc.new" } f.call # control leaves foo here return "return from foo" end 

equivalently

 def foo begin return "return from foo from inside begin/end" } end return "return from foo" end 

therefore, it is clear that the return will simply return from the function 'foo'

in contrast:

 def foo f = proc { return "return from foo from inside proc" } f.call # control stasy in foo here return "return from foo" end 

equivalently (ignoring bindings, as it is not used in this example):

 def unonymous_proc return "return from foo from inside proc" end def foo unonymous_proc() return "return from foo" end 

It also obviously will not return from foo and will not proceed to the next statement.

+6
Sep 17 '09 at 18:29
source share



All Articles