Search code examples
rubyproc-object

Ruby Lambda vs. Proc LocalJumpError


Ruby and StackOverflow newb here working my way through Ruby and ran into my first major roadblock. I'm having a really hard time wrapping my head around Procs and Lambdas. Here is the code I'm working with.

def procBuilder(message)
  Proc.new{ puts message; return}
end

def test
  puts "entering method"
  p = procBuilder("entering proc")
  p.call
  puts "exit method"
end

test

By design, this is to throw a LocalJumpError, but I don't rightly understand why. If I had to guess what this did, I would guess it would initially print "entering proc" upon p = procBuilder("entering proc") running then throw an error on p.call as there is no string being passed by p.call, but clearly I'm missing something critical that is occurring between those 2 lines. I also don't completely understand why this works with a lambda rather than a proc but I imagine understanding the error will resolve that issue as well.

Thanks in advance for the clarification


Solution

  • Here's an answer I gave to a related question. It talks a bit about lambda vs proc and LocalJumpErrors.

    In a proc, return is a special piece of syntax that returns from the lexical scope of the proc, not the proc itself. So it's trying to return out of procBuilder, which has already exited.

    There are a couple ways to fix this:

    1. Don't use return at all. Ruby will return control to proc's caller all on its own.
    2. Change proc to lambda, which behaves the way you expect. Lambdas act like methods; procs act like blocks.

    As for the error you're expecting, you shouldn't get that. procBuilder returns a proc that encloses the message variable. You don't need any arguments to the proc itself.

    Edit: answering your additional question. The proc is a closure. It has "captured" the message variable (a local variable in procBuilder), which was in scope when the proc was created. The proc now can wander through your program with the message variable hidden inside of it, ready to be printed when you call it. The only trouble is the return statement, which has the additional requirement that it the lexical scope still be "live".

    The reason for all this is that this behavior is really helpful in blocks. In this case, it's not helpful at all, so you should just use a lambda, where return means something less insane.

    A really great tutorial on closures in ruby: http://innig.net/software/ruby/closures-in-ruby.rb