@aheejin The Common Lisp condition system has only one operator for executing cleanup forms, named unwind-protect
- it is equivalent to Java's finally
.
However, the Common Lisp condition system works on a completely different basis than e.g. Java exception handling, with the most important difference being: when a Common Lisp condition (this includes errors) is signaled, the stack is wound further instead of being unwound. Only then a transfer of control may (or, in case of error conditions, must) happen.
Under the hood, the Common Lisp condition system is implemented in Common Lisp itself (see example here) on top of its primitive control-flow operators - tagbody
/go
, block
/return-from
, and catch
/throw
.
go
is the "goto" operator that jumps to a label defined in atagbody
;return-from
returns a value from a matchingblock
in lexical scope;throw
returns a value from a matchingcatch
in dynamic scope.
For more detailed information about the CL standard, we could consult CLHS 5.2 Transfer of Control to an Exit Point. Let's copy some text from there:
When a transfer of control is initiated by go, return-from, or throw the following events occur in order to accomplish the transfer of control. Note that for go, the exit point is the form within the tagbody that is being executed at the time the go is performed; for return-from, the exit point is the corresponding block form; and for throw, the exit point is the corresponding catch form.
- Intervening exit points are "abandoned" (i.e., their extent ends and it is no longer valid to attempt to transfer control through them).
- The cleanup clauses of any intervening unwind-protect clauses are evaluated.
- Intervening dynamic bindings of special variables, catch tags, condition handlers, and restarts are undone.
- The extent of the exit point being invoked ends, and control is passed to the target.
- As long as we have dynamic/fluid variables (which, in the worst case, we can implement ourselves on top of lexical variables and a primitive unwind operator), we can resolve point 3 on top of WASM.
- We do not need to care about point 1, since it is undefined behavior in Common Lisp, and so the burden is on the programmer.
- This leaves point 2, which is required to perform nested cleanup forms (think nested
unwind-protect
, which is very common in practice) and point 4, which is the primitive jump operator that we've already used in point 3.
If you need any further Lisp-related help, I'll be available to answer. (Shameless plug: I've described this mechanism in detail in an upcoming book, The Common Lisp Condition System, that will be released soon; I hope that my knowledge in the matter is of help in this issue.)