try , a built-in Tcl command, evaluates a script, evaluates additionally scripts depending on the resulting return code, and then optionally evaluates a final script.
TIP #329 added try to Tcl 8.6. The tcllib module try provides a forward-compatibility implementation of TIP 329 for Tcl 8.5.
Each handler specifies a return code or an -errorcode value, a response script, and a set of variable names that the response script can use to access the return details. An arbitray number of handlers may be specified, and there are currently two types of handler defined:
variableList contains either zero, one or two variable names. The first contains the returned result, and the second contains the return options.
If present, the finally clause's script will be evaluated after the evaluation of the body and whichever handler was selected. It will be evaluated even if the body or the handler produce an error. It will not be evaluated if the interpreter is deleted in the body or handler.
This first example is from the manual page for try, and demonstrates testing for two different error conditions which might arise when attempting to open a file.
try { set f [open /some/file/name] } trap {POSIX EISDIR} {} { puts {failed to open /some/file/name: it's a directory} } trap {POSIX ENOENT} {} { puts {failed to open /some/file/name: it doesn't exist} }
AMG: Here's an example of reraising an error:
try { set f [open /some/file/name] } on error {result options} { puts {something broke!} return -options $options $result }
AMG: [try] can be used to supply a full script where a single command (see [command prefix]) is expected. Simply give it a single argument, that being the entire script to run.
This is a more straightforward alternative to [apply]. It will not produce a new stack frame, so it can directly access local variables with no need for [upvar].
[eval] is the same way but slower. [try] is bytecode-compiled.
Philip Smolen: Example: coroutine x try {yield 3; yield 7; yield 23}
AMG: Building on the above technique, the combination of [tailcall] and [try] can be used in place of [uplevel 1] when the [uplevel] is the final command of a proc or apply body.
[uplevel] only provides access to the caller's variables, whereas [tailcall] actually makes the caller do something, e.g. [return]. As mentioned above, the [try] in there is a shim to make [tailcall] run a script rather than a single command.
HaO 2021-08-23 (thanks to Rich on clt): I first did not understand the reason for the finally clause, as the following two are identical (from the example in the man page):
set f [open /some/file/name a] try { puts $f "some message" } finally { close $f }
and
set f [open /some/file/name a] try { puts $f "some message" } close $f
The magic is, that finally is even executed, if a handler exits the proc. You can execute commands after a return command. And you can overwrite the return value.
Here is a sample session:
proc testProc {finallyReturn} { global list set list "Start" try { error BAM } trap {} {} { lappend list "Trap" return "return from trap" } finally { lappend list "Finally" if {$finallyReturn} { return "return from finally" } } lappend list "End" return "return from proc end" } % testProc 0 return from trap % set list Start Trap Finally % testProc 1 return from finally % set list Start Trap Finally
The interesting case is, that the return value may be set in the trap and the finally clause does not modify it, if there is no return command (case testProc 0).
I only see a use-case for this case. Otherwise, you may just write the finally part after the try command and all is the same.