This is not necessarily the current version of this TIP.
| TIP: | 329 |
| Title: | Try/Catch/Finally syntax |
| Version: | $Revision: 1.2 $ |
| Author: | Trevor Davel <twylite at crypt dot co dot za> |
| State: | Draft |
| Type: | Project |
| Tcl-Version: | 8.6 |
| Vote: | Pending |
| Created: | Monday, 22 September 2008 |
| Discussions To: | http://wiki.tcl.tk/21608 |
| Obsoletes: | TIP #89 |
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 dictionary introduced in Tcl 8.5 by TIP #90.
See TIP #89 for general rationale for enhancing exception handling.
The try...catch syntax presented here is not intended to replace catch, but to simplify the expression of existing exception/error handling techniques, leading to greater code clarity and less error-prone workarounds for finally blocks. There is no deficiency in the functionality of Tcl's exception handling mechanisms - what is lacking is a more readable syntax and a standard for behaviour across packages for the common case of catching a subset errors that are thrown from within a particular block of code.
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 dictionary of options (error information) to be captured. These options supercede the ::errorInfo and ::errorCode globals (though those are still supported for backward compatibility). It is therefore logical to extend/correct the syntax of TIP #89 to support the options dictionary in preference to the older mechanism for capturing exception information.
Benefits of adding this functionality to the core:
Bring to Tcl a construct commonly understood and widely used in other languages.
A standard for identifying categories/classes of errors, which will improve interoperability between packages.
A byte-coded implementation would be significantly faster than the Tcl implementation that is presented.
throw type message
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.
type SHOULD be constructed as a list to maintain compatibility with ::errorcode, but it is treated as a string by [try...catch].
try body ?catch {type ?emvar? ?optvar?} body? ?...? ?finally body?
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. The finally body (if present) will be executed after the try and any catch scripts have been executed, whatever the result of those scripts (excepting resource exhaustion or cancellation).
Returns the result of the last executed body (but not the finally body). If try returns TCL_OK then it will return the result of the try body, otherwise it will return the result of the catch body.
Rules:
The type is a glob that is used to match against the exception's errorcode (-errorcode in the options dictionary, treated as a string).
Only one catch handler will be executed. If the type matches for more than one handler then on the first handler (reading left-to-right in the command) will be executed.
If no matching handler is found then the exception will propagate up the call stack. All return codes other than TCL_ERROR automatically propagate up the call stack.
If the catch body is a literal "-" then the body of the following catch block will be executed instead. (It is an error for the last catch body to be a literal "-".)
When the handler body is executed the error message will be stored in the emvar (if specified) and the return options dictionary in the optvar (if specified). The emvar must be given in order to give the optvar.
If an exception (in fact any return code other than TCL_OK) occurs in a catch block then the new exception takes precedence and will propagate up the stack. The original error stack will be appended to the new errorInfo in order to maintain context.
Irrespective of the outcome of the try or catch bodies that are executed, the finally body (if present) will be executed as the last step before the result is propagated up the call stack (unless the reason for the propagation is the breaching of a resource limit or the complete cancellation of an interpreter). If the result of the finally body is anything other than TCL_OK, that result will take precedence.
Simple example of try/catch/finally logic in Tcl using currently available syntax:
proc read_hex_file {fname} {
set f [open $fname "r"]
set data {}
set code [catch {
while { [gets $f line] >= 0 } {
append data [binary format H* $line]
}
} em opts]
if { $code != 0 } {
dict set opts -code 1
set em "Could not process file '$fname': $em"
}
close $f
return -options $opts $em
}
And the same example rewritten to use [try...catch...finally]:
proc read_hex_file {fname} {
set f [open $fname "r"]
set data {}
try {
while { [gets $f line] >= 0 } {
append data [binary format H* $line]
}
} catch {* em} {
error "Could not process file '$fname': $em"
} finally {
close $f
}
}
This illustrates how the intent of the code is more clearly expressed by [try...catch], but does not demonstrate the use of multiple catch blocks.
Tcl 8.4 catch [1]
Various alternatives are discussed on the wiki [2] along with reasons for their rejection.
No support is provided for catching return codes other than TCL_ERROR. Support may be added in future via an alternative keyword to catch (say catchcode).
A prototype implementation is available on the wiki [3].
This document has been placed in the public domain.
This is not necessarily the current version of this TIP.