Interactions between bind-exit and unwind-protect are complicated, especially when a non-local exit is taken during the execution of an unwind-protect cleanup form. This design note replaces bind-exit and unwind-protect with a new block construct, and clarifies Dylan's behavior.
Dylan Design Notes
Dylan Design Notes: #25: Exit Extent (Change)
#25: Exit Extent (Change)Version 1, January 1994 Copyright (c) 1993-1994, Apple Computer
block ([exit-var]) exprs [cleanup cleanup-clauses] [Special Form] => valuesblock executes the exprs in sequence, and then the optional cleanup-clauses. Normally, the value returned by block is the value of the last expr. If there are no exprs, #f is returned.
If exit-var is provided, exit-var is bound to an exit procedure during the execution of the exprs and cleanup-clauses. At any point in time before the last cleanup-clause returns, the exit procedure can be called. Calling the exit procedure has the effect of immediately terminating the execution of exprs. The exit procedure accepts any number of arguments. These arguments are used as the return values of block. Calling an exit procedure is known as performing a non-local exit.
Generally, the cleanup-clauses are guaranteed to be executed. Even if one of the exprs is terminated by a non-local exit out of the block, the cleanup-clauses are executed before the non-local exit can complete. For example, the following code fragment ensures that files are closed even in the case of an error causing a non-local exit:
block (return) open-files(); ... if (error) return(#f); end if; ... result cleanup close-files(); end blockHowever, if one of the cleanup-clauses is terminated by a non-local exit out of the block, any following cleanup-clauses within the same block are not executed.
In the following example, the block establishes an exit procedure and binds bar to that exit procedure. The block returns an anonymous method containing bar, which is then bound to foo. Calling the foo method is an error because it is no longer valid to invoke bar after its establishing block returns.
? define foo block (bar) method (n) bar(n); end block; end foo. ? foo(5) error or other undefined consequencesWhen an exit procedure is called, it initiates a non-local exit out of its establishing block. Before the non-local exit can complete, however, the cleanup clauses of intervening blocks (blocks that have been entered, but not exited, since the establishing block was entered) must be executed, beginning with the most recently entered intervening block. Once the cleanup clauses of an intervening block have been executed, it is an error to invoke the exit procedure established by that block. Finally, the cleanup clauses of the establishing block are executed, further invocation of the exit procedure becomes invalid, and the establishing block returns with the values that were passed to the exit procedure.
During the process of executing the cleanup clauses of the intervening blocks, any valid exit procedure may be invoked and may interrupt the current non-local exit.
block (one) block (two) two(2); cleanup one(1); end; 3 endThe following expression is valid and returns 2. The exit procedure bound to exit is still valid when the cleanup is executed.
block (exit) exit(1); cleanup exit(2); endThe following expression returns 1 rather than 2. The second cleanup is never executed.
block (exit) 3; cleanup exit(1); exit(2); endThe following expression returns 2. Nesting the blocks ensures that both cleanups will be executed.
block (exit) block () 3; cleanup exit(1); end; cleanup exit(2); endThe following expression is valid and returns 3. The call to one does not destroy information needed to invoke two.
block (one) block (two) one(1); cleanup two(2); end; 3 end
Next chapter: #26: New Iteration Protocol (Change)