TIP proposal: Try/Catch Exception Handling
Draft proposal by Twylite 2008-09-12.
This TIP proposes the addition of new core commands to improve the exception handling mechanism. It supercedes TIP #89 by providing support for the error options dict introduced in Tcl 8.5 by TIP #90.
See TIP #89 for general rationale for enhancing exception handling.
In Tcl 8.4 exceptions could be caught using [catch], and exception information was available via the [catch] return value and resultvar. If the return value was TCL_ERROR (1) then the globals ::errorCode and ::errorInfo would be set according to the exception raised.
TIP #89 was written to work with this model, such that a catch handler (in a try...catch) would be able to capture the resultvar, errorCode and errorInfo.
Tcl 8.5 implements TIP #90 which extends [catch] to allow an additional dict of options (error information) to be captured. These options supercede the ::errorInfo and ::errorCode globals.
It it logical to extend/correct the syntax of TIP #89 to support the options dict in preference to the older mechanism for capturing exception information.
Since the catch handlers in the try...catch control structure will filter based on the exception's errorcode, it makes sense to have a command that will encourage the use of error codes when throwing an exception. [throw] is merely a reordering of the arguments of the [error] command.
TBD Use within a catch handler to rethrow the existing exception, possibly with translation of the errocode and message. The intent is to allow exception translation/chaining without losing the context of the original error.
The try body is evaluated in the caller's scope. If the result is TCL_ERROR then each catch handler is considered in order until one is found with a type that matches the exception's errorcode, then the body of that handler is executed.
Rules:
KD: What I'm missing in this is that Tcl has already a mechanism for throwing exceptions, namely special return codes. For example, the following throws an exception with return code 7:
return -code 7 "User provided incomplete input"
which can be catched somewhere on the call tree with:
switch [catch {do something} result] { 0 {do the next thing} 1 {return -code 1 -errorcode $::errorCode -errorinfo $::errorInfo $result} 7 {tk_messageBox -type ok -icon error -message $result} }
Tip 89 seems to use ::errorCode for everything, but exceptions are not errors, and currently ::errorCode in Tcl is used only for I/O and OS errors. To me it seems better to keep return code 1 for these errors, and use special return codes for user-defined exceptions.
NEM: Should try be able to catch non-error exceptions (e.g. break/continue)? Should throw be able to create them? Throw and rethrow need an options dict argument, or take options separately (e.g. -errorinfo). Is type considered to be a string or a list? An exception in a catch handler should abort with error immediately -- don't keep searching through other handlers, as they might throw errors too. I'd say execute only first matching catch handler. Given that type matching is glob-style, the catch handler may also want to capture it so it can determine what exact errorCode matched. Drop the RETHROW errorCode idea - I can't see a good use-case for this. try should return the result of its body. If an error is thrown and caught, then the result of the corresponding catch block should be returned, allowing catch blocks to implement defaults.
Finally, my ideal would be that try/catch creates a lambda(s) for catch handlers and these are invoked at the point of error, rather than when the stack has unwound, as this allows for resumes (via break/ continue). This isn't possible to do for errors in general, however (e.g. C code errors).