random-state.net

Nikodemus Siivola

<< next | top | previous >>

Condition Style Guide #
hacking, June 3rd 2009

I don't claim this to be authoritative, but I'd be very interested in hearing any opposing views. So: a quick style guide for using the Common Lisp condition system — I'm eliding restarts for now, though.

Signalling Conditions

Use #'ERROR to signal conditions inheriting from SERIOUS-CONDITION. This includes all subclasses or ERROR. SERIOUS-CONDITION is a convention that means "this will land you in the debugger if not handled", which is what #'ERROR ensures.

  • Inherit from ERROR if unwinding from the error leaves the system in a consistent state. Subclasses of ERROR mean "oops, we have a problem, but it's OK to unwind."
  • Inherit from SERIOUS-CONDITION, not ERROR, if the system is messed up somehow, and/or requires either human intervention or special knowledge about the situation to resolve: SERIOUS-CONDITIONs that are not ERRORs mean that "something is deeply wrong and you should know what you are doing if you are going to resolve this."

Use #'WARN to signal conditions inheriting from WARNING. This includes all subclasses of STYLE-WARNING. WARNING is a convention that means "this will print out a warning if not muffled, and you can muffle this with the MUFFLE-WARNING restart", which is what #'WARN ensures.

  • Inherit from STYLE-WARNING if muffling the warning should be fine pretty much whenever.
  • Inherit from WARNING, not STYLE-WARNING, if muffling the warning is OK only if the muffler knows how to deal with the specific warning: otherwise someone should be made aware of the situation, which is what warnings are for.

Otherwise use #'SIGNAL, and don't expect others to handle your conditions unless you document them as part of your API.

Handling Conditions

Learn the difference between HANDLER-BIND and HANDLER-CASE. Short version: HANDLER-CASE always unwinds, with HANDLER-BIND you can eg. log the condition without handling it, or decide whether to unwind or not after inspecting the condition in more detail.

Don't handle arbitrary conditions: a lower layer using #'SIGNAL as a communication mechanism will break if you do that.

Don't muffle arbitary WARNINGs that are not STYLE-WARNINGS. A full warning can still be muffled, but only if it's one you know to be harmless in your case. Otherwise, a full warning should either result in a fail-stop, or logging the condition somewhere — depending on the kind of application you are developing.

Don't handle arbitary SERIOUS-CONDITIONs that are not ERRORs: if you cannot pop up a debugger and don't know exactly how to deal with this specific condition, the right answer is to crash — of course preferably with an error message, but don't expect that to work properly: maybe the serious condition was about corruption in the IO system...

If you just want to ensure the application doesn't enter the debugger, bind *DEBUGGER-HOOK* appropriately — but be aware that #'BREAK circumvents *DEBUGGER-HOOK*: if that's a danger, refer to the documentation of the implementation you are using — most provide a way to hook into #'BREAK.

Happy hacking.