TIP:            329
Title:          Try/Catch/Finally syntax
Version:        $Revision: 1.9 $
Author:         Trevor Davel <twylite@crypt.co.za>
State:          Final
Type:           Project
Vote:           Done
Created:        22-Sep-2008
Post-History:   
Discussions-To: http://wiki.tcl.tk/21608
Obsoletes:      89
Tcl-Version:    8.6

~ Abstract

This TIP proposes the addition of new core commands to improve the exception
handling mechanism. It supercedes [89] by providing support for the error
options dictionary introduced in Tcl 8.5 by [90].

~ Rationale

See [89] for general rationale for enhancing exception handling.

The '''try''' 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 or exceptions 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. [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 [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 [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.

~ Specification

 > '''try''' ''body'' ?''handler'' ...? ?'''finally''' ''body''?

 > '''throw''' ''type message''

The '''try''' body is evaluated in the caller's scope. The handlers are 
searched in order of declaration until a matching one is found, and the 
associated body is executed. If no matching handler is found then '''try''' 
returns the result of the '''try''' body (exceptions will propagate up the 
stack as usual); otherwise '''try''' returns the result of the handler body
(exceptions will propagate up the stack as usual).

Only one handler body (that of the first matching handler) will be executed. If 
the handler body is the literal string "'''-'''" then the body for the 
subsequent handler will be used instead. It is an error for the last handler's 
body to be a literal "'''-'''".

The '''finally''' body (if present) will be executed last, and is always
executed whatever the results of the try and handler bodies (excepting
resource exhaustion or cancellation). If the '''finally''' body returns an
exceptional code then this will become the result of '''try''', otherwise the
result of the '''finally''' body is ignored.

Since the '''trap''' handlers in the '''try''' control structure are filtered
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'' is treated as a list by '''trap''' (see below), which
maintains compatibility with the description of ::errorCode given in
'''tclvars'''.

~~Handlers

Each handler is identified by a keyword. The fields following the keyword 
indicate what exceptions or errors are matched by the handler, the variables 
into which the result of the '''try''' body will be assigned (in the caller's 
scope), and the body of the handler.

 > '''on''' ''code {?resultVar ?optionsVar?} body''

The '''on''' handler allows exact matching against the exceptional return code
(the integer value that would be returned by '''catch'''). The ''code'' may be 
given as an integer or one of the magic keywords '''ok''' (0), '''error''' (1),
'''return''' (2), '''break''' (3), '''continue''' (4).

 > '''trap''' ''pattern {?resultVar ?optionsVar?} body''

The '''trap''' handler allows list prefix matching against the
'''-errorcode''' from the options when the exceptional return code is
TCL_ERROR (1). Given a ''pattern'' and an ''errorcode'', a list prefix match
is successful if for every element in ''pattern'' there is a corresponding and
identical element in ''errorcode''. Trailing elements in ''errorcode'' are
ignored.

Notes & clarifications:

 * Handlers are searched in order of declaration (left-to-right). One
   consequence of this search order is that an '''on error''' handler will
   supercede all subsequent '''trap''' handlers.

 * Any unhandled exception propagates.

 * The result of the last executed body (other than the '''finally''' body) is
   the result of the '''try'''. Exceptions in any ''handler'' body or in the
   '''finally''' replace the existing exception and propagate.

 * If any exception is replaced (by an exception in a handler body or in the
   '''finally''' body) then the new exception shall introduce into its options
   dictionary the field '''-during''' that contains the options dict of the
   exception that was replaced.

 * If any errorcode happens to be not a list, a '''trap''' handler will be
   unable to process it. However, this should only happen in cases where there
   is a bug or other problem elsewhere, since '''return''' is documented to
   require the errorcode to be a list.

~ Examples

Simple example of '''try'''/''handler''/'''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''']]:

| proc read_hex_file {fname} {
|    set f [open $fname "r"]
|    set data {}
|    try  {
|       while { [gets $f line] >= 0 } {
|         append data [binary format H* $line]
|       }
|    } trap {POSIX} {} {
|       puts "POSIX-type error"
|    } on error {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''']].

~ References

 * Tcl 8.4 catch [http://www.tcl.tk/man/tcl8.4/TclCmd/catch.htm]

 * Tcl 8.5 catch [http://www.tcl.tk/man/tcl8.5/TclCmd/catch.htm]

~ Rejected Alternatives

Various alternatives are discussed on the wiki [http://wiki.tcl.tk/21608]
along with reasons for their rejection.

~ Future Extensions

No specific future exceptions are planned, but '''try''' could be extended
by adding new handler keywords and/or introducing new varnames to the variables
list that is associated with each handler.

It is recommended that new handlers maintain the established convention:

 > '''keyword''' ''criteria {?resultVar ?optionsVar?} body''

~ Reference Implementation

A reference implementation can be found at [http://www.crypt.co.za/pub/try-1.tcl]

~ Thanks

Thanks in particular to DKF, NEM and JE for their feedback and suggestions
on this TIP.

~ Copyright

This document has been placed in the public domain.
