11

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.
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
voltair
  • 615
  • 2
  • 7
  • 21
  • possible duplicate of [Using 'return' in a Ruby block](http://stackoverflow.com/questions/2325471/using-return-in-a-ruby-block) – mgibsonbr Mar 22 '13 at 05:01
  • 1
    Here: http://stackoverflow.com/questions/1435743/why-does-explicit-return-make-a-difference-in-a-proc – fmendez Mar 22 '13 at 05:10
  • I know you are learning this from codeacademy. I have the same doubts. Its a decent introductory tutorial, but you have to search google a LOT to be able to understand the tutorial. – sid smith Sep 20 '14 at 20:03

2 Answers2

9

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.
Community
  • 1
  • 1
mgibsonbr
  • 21,755
  • 7
  • 70
  • 112
  • 1
    given that this is the case, how do method such as find, which accept blocks with logic tests, use the results of those logic tests? (example: [1,2,3,4,8,10].find {|num| num*num>24} ) – voltair Mar 22 '13 at 05:30
  • @user1419674 see the updated answer. The same way your example code did not use `return`, you don't have to use it either to access the evaluation of the block. You can just use the return value from `call`, using it directly (as in my example code) or assigning it to a variable (ex.: `value = block.call`) – mgibsonbr Mar 22 '13 at 05:55
  • This is pretty strange to me, but I believe this link provides an answer to the question. [link]http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_containers.html It actually says that yield can provide a return value. What strikes me as so odd about this is that in this case it would seem that an explicit return and an implicit return produce different results within the block. – voltair Mar 22 '13 at 06:18
  • Here is the relevant excerpt from the link: "A block may also return a value to the method. The value of the last expression evaluated in the block is passed back to the method as the value of the yield. This is how the find method used by class Array works.[The find method is actually defined in module Enumerable, which is mixed into class Array.] Its implementation would look something like the following." – voltair Mar 22 '13 at 06:18
  • @user1419674 thanks for pointing out that `yield` can return a value, I updated the answer. About the "strangeness", I also feel this way about this feature, but at least the specs are clear: the `return` keywords is relative to the *defining* context, not the *calling* one. – mgibsonbr Mar 22 '13 at 21:37
  • @mgibsonbr - the quote is confusing. Please tell me what these things mean : `current context`, `closure`. Thanks. – sid smith Sep 20 '14 at 20:49
  • @sidsmith I'm not familiar enough with Ruby to know *exactly* how execution contexts work in that language, but I'm assuming is similar enough to [JavaScript's](http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/). And [closure](http://en.wikipedia.org/wiki/Closure_%28computer_programming%29) roughly means when a function declared within another function has access to local variables of the outer function (note that I said "declared", not "called" - it's the lexical scope that matters, i.e. whether one function occurs inside another *in the source code*). – mgibsonbr Sep 20 '14 at 21:19
1

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.

sethi
  • 1,869
  • 2
  • 17
  • 27