Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

TIP #329: Try/Catch/Finally syntax

10 views
Skip to first unread message

Donal K. Fellows

unread,
Sep 23, 2008, 5:42:16 AM9/23/08
to

TIP #329: TRY/CATCH/FINALLY SYNTAX
====================================
Version: $Revision: 1.2 $
Author: Trevor Davel <twylite_at_crypt.co.za>
State: Draft
Type: Project
Tcl-Version: 8.6
Vote: Pending
Created: Monday, 22 September 2008
URL: http://www.tcl.tk/cgi-bin/tct/tip/329.html
Discussions-To: http://wiki.tcl.tk/21608
Post-History:
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:

* 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
===============

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

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
============

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

REJECTED ALTERNATIVES
=======================

Various alternatives are discussed on the wiki
[<URL:http://wiki.tcl.tk/21608>] 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
[<URL:http://wiki.tcl.tk/21608>].

COPYRIGHT
===========

This document has been placed in the public domain.

-------------------------------------------------------------------------

TIP AutoGenerator - written by Donal K. Fellows

0 new messages