A return inside a function should end just that function and hand its value to the caller - not halt the whole program. Today you unwrap the return value at the call boundary to make that so.
Unwrap a return value at the function boundary so an early return ends the function, not the program.
Back in the evaluation chapter you made return wrap its value so it bubbles up
through blocks. Now that functions exist, that wrapper needs to be unwrapped at
the function boundary: after evaluating a function body, if the result is a
ReturnValue, return the plain value inside it to the caller. The wrapping is
what lets a return deep inside if blocks stop the body; the unwrapping is what
keeps it from stopping the program.
So return x; x + 1; inside f yields 5 and skips the x + 1, and a return
from inside a nested if still exits just that function. This boundary is why the
value stays wrapped as it rises through blocks but becomes ordinary once it
crosses out of a call - two behaviors from one mechanism.
// after Eval(fn.Body, env) returns:if rv, ok := result.(*ReturnValue); ok {return rv.Value // unwrap: the function yields the plain value}return result