One of Lisp's great features is its condition system.
Every Common Lisp implementation has a condition system.
... conditions are more general than exceptions in that a condition can represent any occurrence during a program's execution that may be of interest to code at different levels on the call stack.
The following three quotes are abridged from a conversation with Ed Langley over #common-lisp:matrix.org (Thu Dec 23 07:46:30 PM EST 2021)
Why is condition handling special in common lisp compared to other languages?
Because code can define recovery strategies without handling the condition.
Any bit of code can be wrapped with restart-case to define a restart. This restart defines a way you could continue from any condition that’is signaled within the dynamic extent of the restart-case.
Your condition handler can do something like (find-restart ‘continue c) to find the CONTINUE restart associated with condition C. If the restart exists, it can use invoke-restart to jump to the restart which runs and then execution continues like normal after the restart-case form.
-- end of coversation
Restarts are the choices we get in the debugger, which always has the RETRY and ABORT ones. By handling restarts we can start over the operation as if the error didn't occur (as seen in the stack).
(divide 3 0) ;; Y can not be zero. Please change it ;; [Condition of type SIMPLE-ERROR] ;; ;; Restarts: ;; 0: [CONTINUE] Retry assertion with new value for Y. <--- new restart ;; 1: [RETRY] Retry SLIME REPL evaluation request. ;; …
both, production code and debugging. Every fault that you handle manually using the debugger can be written down into some Lisp code; put that code inside a handler function and wrap a handler-bind around your toplevel and bam, you have achieved automatic error handling with a bit of clever design. You never even need to kill your program to add new handlers. It can simply keep on running™
- phoe (#common-lisp:matrix.org, feb 10)