A problem in Tcl (which is addressed, in various ways, by [TIP] #90 [http://www.tcl.tk/cgi-bin/tct/tip/90.html]) is that the '''-code''' option to [return] has certain limitations; in particular it hides the final return code specified by this option in a place where Tcl scripts cannot access it, and so if a [[return -code]] is caught before it has caused a procedure body to return then some information will be lost. One consequence of this is that the two procedures defined by proc a {} {while 1 {return -code error}} package require control proc b {} {control::do {return -code error} while 1} display different behaviour. '''a''' returns with an error on the first iteration. '''b''' returns without an error on the first iteration. In some sense this difference is a deficiency in control::do, but there is in fact no way to implement it so that it handles this. The language does not support it! Below are defined some commands that provide a workaround for this. The idea is to have a command [[returneval]] which not only causes the calling procedure to return but also evaluates a user-supplied command "in place of" the procedure that returned. This lets one define a procedure '''c''' through eproc c {} {control::do {returneval {error}} while 1} that behaves just like '''a'''. In particular, catch {a} catch {c} both return 1 whereas catch {b} returns 0. What one must keep in mind when considering these matters is how Tcl command return codes work in general, and the manner in which the '''return''' return code (2) works in particular. It is really quite simple, but as it is also easily mistaken for being magical, many people are confused by it. Most Tcl commands make a distinction only between two classes of return codes: the '''ok''' return code (0), and the other return codes. If they get an '''ok''' return code from some recursive invocation of the Tcl interpreter then they do what they usually do. If they get any other return code then they return themselves, passing the nonzero return code (and the corresponding return value / error message) back down to whatever called the interpreter that time. The main exception is of course the [catch] command, since it just returns the return ''code'' of the script it recursively invoked as its return ''value'', whereas its return code is the normal 0. This quick propagation of a nonzero return code is how errors are transferred back to the outermost caller. This is also how [return] works: the return code of this command is nonzero (2, to be precise) and the commands surrounding a [return] will therefore quickly pass this along. If it reaches a [catch] then it will be caught (and the return value of that [catch] will be 2), but it usually doesn't. What makes the [return] command different from the [error] command in normal usage is however that procedure bodies treat the corresponding return codes differently. An '''error''' return code (1) is passed along, but a '''return''' return code is intercepted and it will usually change to something different. A simple [[return]] or [[return -code ok]] will get the return code '''ok''' (0) when they meet a procedure body. A [[return -code error]] will get the return code '''error''' (1) when it meets a procedure body. A [[return -code break]] will get the return code '''break''' (3) when it meets a procedure body. And so on. Something similar happens with the [break] and [continue] commands, although the return codes produced by these are intercepted not only by [catch] and procedure bodies, but also by the loop commands ([for], [foreach], [while], ...). On the other hand, the [uplevel] command does not intercept any return codes. The [returneval] command makes use of the nonstandard return code -1. The reason for choosing this can be found on the [uplevel] page. proc returneval {script} {return -code -1 $script} However, this will just behave as an [error] if it isn't intercepted at some point. Therefore ''any procedure which you might want to returneval from must be defined using '''eproc''' rather than '''proc'''.'' proc eproc {name arglist body} { interp alias {} $name {} eproc_call "$name " proc "$name " $arglist $body } proc eproc_call {args} { set code [catch [list uplevel 1 $args] res] if {$code == -1} then { set code [catch [list uplevel 1 $res] res] return -code $code $res } elseif {$code == 1} then { global errorInfo errorCode return -code error -errorinfo $errorInfo -errorcode $errorCode $res } else { return -code $code $res } } [[Also explain why the above works]] ---- '''[DGP]''' Very nice. This could possibly be a way to work around the limitations of the control package commands until a TIP 90 solution is in place. Some nitpicking: it looks like [[eproc]] assumes it is called from the :: namespace. To make this robust, there should be an [[uplevel 1 ::namespace current]] to discover the namespace context of the caller, then be careful to create both the alias and the proc in that namespace. '''Lars H''': I suspect you're right about that. Would it work to simply [uplevel] the commands in '''eproc''', i.e., proc eproc {name arglist body} { uplevel 1 [list interp alias {} $name {} ::eproc_call "$name "] uplevel 1 [list proc "$name " $arglist $body] } ? '''[DGP]''' For the [[proc]], yes. For the [[interps alias]] no. For no apparently good reason that I can fathom, [[proc]]s get defined in the current namespace while aliases get defined in the namespace :: of the target interp.