Previous section: Iteration Constructs

Dylan reference manual -- Non-local Exits

Non-local Exits

block  ( [exit-var] ) 	[Special Form]
		body 
		[cleanup cleanup-clause  | exception exception-clause ]*
	end [ block ]
  =>  values
block is a construct which combines the functionality of non-local exits, protected forms, and exception handling. block executes the expressions in the body in sequence, and then the optional cleanup-clauses. Any number of cleanup-clauses may be provided, and they may be arbitrarily interleaved with exception-clauses (described in the chapter on Conditions). Normally, the value returned by block is the value of the last expression in the body. If there are no expressions in the body, #f is returned.

If exit-var is provided, exit-var is bound to an exit procedure during the execution of the body and clauses. At any point in time before the last clause returns, the exit procedure can be called. Calling the exit procedure has the effect of immediately terminating the execution of the body. 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 expressions in the body 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 from the block body:

block (return)
  open-files();
  ...
  if (error)
    return(#f);
  end if;
  ...
  result
cleanup
  close-files();
end block
However, 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.

Restrictions on the use of exit procedures

The exit procedure is a first-class object. Specifically, it can be passed as an argument to functions, stored in data structures, etc. Its use is not restricted to the lexical body of the establishing block (the block in which that exit procedure was established). However, invocation of the exit procedure is valid only during the execution of the establishing block. It is an error to invoke an exit procedure after its establishing block has returned, or after execution of the establishing block has been terminated by a non-local exit.

In the following example, the block establishes an exit procedure and binds bar to that exit procedure. The block returns an anonymous method containing a call to bar. The anonymous method 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 constant foo =
    block (bar)
       method (n) bar(n) end;
    end block;
? foo(5)
error or other undefined consequences

When 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.

Next section: 5. Comparisons