TIP #329 Version 1.2: Try/Catch/Finally syntax

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

Abstract

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.

Rationale

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:

Specification

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:

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.

Examples

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.

References

Rejected Alternatives

Various alternatives are discussed on the wiki [2] along with reasons for their rejection.

Future Extensions

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).

Reference Implementation

A prototype implementation is available on the wiki [3].

Copyright

This document has been placed in the public domain.


Powered by TclThis is not necessarily the current version of this TIP.

TIP AutoGenerator - written by Donal K. Fellows