return inside a lambda vs inside a proc
Question
What's the return value of the following Ruby code?
def procs_return
-> { return :lambda }.call
proc { return :proc }.call
return :return
end
procs_return # => ???
The correct answer is
-
:lambda -
:procCorrect -
:return -
It raises
LocalJumpError
Explanation
TL;DR
return inside a lambda returns from the lambda only. return inside a proc returns from the method where the proc was defined. So the lambda call completes and the method keeps going, then the proc's return :proc exits procs_return itself. The final return :return never runs and the method returns :proc.
Step by step
def procs_return
-> { return :lambda }.call
proc { return :proc }.call
return :return
end
procs_return # => :proc
The lambda behaves like a miniature method: its return just produces the lambda's own return value, and execution resumes on the next line of procs_return:
def lambda_only
result = -> { return :lambda }.call
[result, :method_continues]
end
lambda_only # => [:lambda, :method_continues]
The proc has no such boundary. Its return belongs to the enclosing method, exactly as if it had been written inline in the method body. When proc { return :proc }.call runs, procs_return returns :proc on the spot.
Both objects are instances of Proc; the flag that changes the semantics is visible through Proc#lambda?:
-> {}.lambda? # => true
proc {}.lambda? # => false
Edge cases
A proc's return targets the method frame that created the proc. If that frame is already gone when the proc is called, there is nothing to return from and Ruby raises LocalJumpError:
def make_proc
proc { return :oops }
end
orphan = make_proc
begin
orphan.call
rescue LocalJumpError => e
e.message # => "unexpected return"
end
This is why :proc beats the LocalJumpError choice here: the proc in the snippet is called while procs_return is still on the stack, so its return has a live method to unwind.
Share this quiz
Comments
No comments yet. Be the first.