Unwind-protect has a straightforward semantics for programming languages where non-local control jumps are purely first-order, i.e., computations can abort to a dynamically enclosing context, but can never re-enter an already exited context. Such languages include Common Lisp, Java, C++, and even text-editor languages like Emacs and Vim; all of them provide unwind-protect. An unwind-protected block of code B has a postlude P that is guaranteed to run whenever and however B exits, whether normally or via a non-local exit to some enclosing dynamic context. This is a useful guarantee to have, as we can have P encode clean-up actions that we can rely upon to happen. The canonical use for unwind-protect is to ensure that file ports opened in B get closed when B is exited.
(let ([o #f]) (unwind-protect ;protected code (begin (set! o (open-output-file "file")) ... <possible non-local exit> ... ) ;the postlude (close-output-port o)))
When we move to higher-order control scenarios such as Scheme [8] and ML [1], it is no longer clear what unwind-protect should mean. Here, control can re-enter a previously exited dynamic context, opening up new questions such as:
Should a prelude be considered in addition to the postlude?
Can the postlude be evaluated more than once?
Should the postlude be enabled only for some exits but not for others, and if so which?
The language Scheme provides a related operator called dynamic-wind that attaches a prelude and a postlude to a code block, and ensures that the postlude (prelude) is always evaluated whenever control exits (enters) the block. While this may seem like a natural extension of the first-order unwind-protect to a higher-order control scenario, it does not tackle the pragmatic need that unwind-protect addresses, namely, the need to ensure that a kind of ``clean-up'' happens only for those jumps that significantly exit the block, and not for those that are minor excursions. The crux is identifying which of these two categories a jump falls into, and perhaps allowing the user a way to explicitly fix the category. It usually makes no sense to re-enter a block after the clean-up has been performed (as in the port-closing example above): Thus there is no need for a specific prelude syntax beyond sequencing, and postludes need happen only once. Thus we can answer questions 1 and 2 above with No, but there is no single objectively correct answer to question 3.