return(3tcl)

πŸ’– return β€” the Tcl Built-In Command that yeets you out of a proc πŸ’–

ngl bestie, we're deep in return(3tcl) territory β€” that's the Tcl Built-In Commands manual, and this page is a whole saga so it comes in two parts. Sit down, grab a strawberry πŸ“, we're doing this.

✨ NAME ✨

return β€” bounce out of a procedure, or set the return code of a script. that's it, that's the job. πŸ‘‘


✨ SYNOPSIS ✨

return ?result?
return ?-code code? ?result?
return ?option value ...? ?result?

🌸 DESCRIPTION 🌸

Okay so the simplest possible vibe: you just call return with no options at all, inside the body of a procedure, and it immediately hands control back to whoever called that procedure. bye bestie, we're out 🫢

If you pass a result argument, then that value becomes the result of the procedure, passed back to the caller. And if you don't specify result? Then an empty string goes back to the caller as the procedure's result. empty. void. the silence after a read receipt. 😭

return also does a very similar thing inside script files that get evaluated by the source command. When source is out here evaluating the contents of a file as a script, and somebody hits return, script evaluation immediately ceases β€” like, no more lines, we're done β€” and the value result (or an empty string if you gave it nothing) becomes the result of the source command. iconic behaviour honestly.


πŸ’… EXCEPTIONAL RETURN CODES πŸ’…

So besides just handing back a result, return can also set the return code of a procedure β€” and it does that with the -code option. Normally, when you don't specify -code, the procedure returns like a normal, well-adjusted procedure. But -code lets you generate an exceptional return. 😳

And code can be any of these little guys:

  • ok (or 0) β€” Normal return: literally the same as if you never passed the option at all. The return code of the procedure is 0 (TCL_OK).
  • error (or 1) β€” Error return: the return code of the procedure is 1 (TCL_ERROR). In its calling context, the procedure command behaves exactly as if it were the command error result. More on the extra options for this below πŸ‘€
  • return (or 2) β€” The return code of the procedure is 2 (TCL_RETURN). In its calling context, the procedure command behaves as if it were the command return with no arguments.
  • break (or 3) β€” The return code of the procedure is 3 (TCL_BREAK). In its calling context, the procedure command behaves as if it were the command break.
  • continue (or 4) β€” The return code of the procedure is 4 (TCL_CONTINUE). In its calling context, the procedure command behaves as if it were the command continue.
  • value β€” value must be an integer; it gets returned as the return code for the current procedure. Applications and packages should use values in the range 5 to 1073741823 (0x3fffffff) for their own purposes. Values outside this range are reserved for use by Tcl. so don't go rogue with numbers, bestie πŸ™ˆ

When a procedure wants to signal that it got invalid arguments from its caller, it can use return -code error with result set to a suitable error message. Other than that, usage of the return -code option is mostly limited to procedures that implement a whole new control structure. that's the flex πŸ’ͺ

The return -code command acts similarly within script files that are evaluated by the source command. During the evaluation of the contents of a file as a script by source, an invocation of the return -code code command will cause the return code of source to be code.

πŸŽ€ RETURN OPTIONS πŸŽ€

Okay so besides a result and a return code, evaluating a command in Tcl also produces a dictionary of return options. In general usage, all the option/value pairs you hand to return become entries in that return options dictionary, and honestly any values at all are acceptable β€” except as noted below, obviously, there's always a fine print πŸ“œ

The catch command is what you use to capture all of this: the return code, the result, and the return options dictionary, all coming out of a script evaluation. catch is the group chat screenshot of Tcl. πŸ’€

As documented above, the -code entry in the return options dictionary gets special treatment from Tcl. But it's not the only one β€” here are the other return options Tcl recognizes and treats specially:

  • -errorcode list β€” The -errorcode option only gets special treatment when the value of the -code option is TCL_ERROR. Then the list value is meant to be extra information about the error, presented as a Tcl list for further processing by programs. If no -errorcode option is provided to return when the -code error option is provided, Tcl will set the value of the -errorcode entry in the return options dictionary to the default value of NONE. The -errorcode return option will also be stored in the global variable errorCode.
  • -errorinfo info β€” The -errorinfo option only gets special treatment when the value of the -code option is TCL_ERROR. Then info is the initial stack trace, meant to give a human reader extra info about the context in which the error occurred. The stack trace will also be stored in the global variable errorInfo. If no -errorinfo option is provided to return when the -code error option is provided, Tcl will provide its own initial stack trace value in the entry for -errorinfo. Tcl's initial stack trace will include only the call to the procedure, and stack unwinding will append information about higher stack levels β€” but there will be no information about the context of the error within the procedure. Typically the info value is supplied from the value of -errorinfo in a return options dictionary captured by the catch command (or from the copy of that information stored in the global variable errorInfo).
  • -errorstack list β€” The -errorstack option only gets special treatment when the value of the -code option is TCL_ERROR. Then list is the initial error stack, recording actual argument values passed to each proc level. The error stack will also be reachable through info errorstack. If no -errorstack option is provided to return when the -code error option is provided, Tcl will provide its own initial error stack in the entry for -errorstack. Tcl's initial error stack will include only the call to the procedure, and stack unwinding will append information about higher stack levels β€” but again, there will be no information about the context of the error within the procedure. Typically the list value is supplied from the value of -errorstack in a return options dictionary captured by the catch command (or from the copy of that information from info errorstack).
  • -level level β€” The -level and -code options work together to set the return code that gets returned by one of the commands currently being evaluated. The level value must be a non-negative integer representing a number of levels on the call stack. It defines the number of levels up the stack at which the return code of a command currently being evaluated should be code. If no -level option is provided, the default value of level is 1, so that return sets the return code that the current procedure returns to its caller β€” one level up the call stack. The mechanism by which these options work is described in more detail below πŸ‘‡
  • -options options β€” The value options must be a valid dictionary. The entries of that dictionary are treated as additional option/value pairs for the return command. nesting doll behaviour, we love to see it πŸͺ†

🌈 RETURN CODE HANDLING MECHANISMS 🌈

Return codes are how Tcl controls program flow. A Tcl script is just a sequence of Tcl commands. As long as each command evaluation returns a return code of TCL_OK, evaluation just keeps rolling on to the next command in the script. But any exceptional return code (anything that isn't TCL_OK) returned by a command evaluation interrupts the flow to the next command. Script evaluation ceases, and the exceptional return code from the command becomes the return code of the full script evaluation. βœ‹

This is the mechanism by which errors during script evaluation cause an interruption and an unwinding of the call stack. It's also the mechanism by which commands like break, continue, and return cause script evaluation to terminate without evaluating all the commands in sequence. sneaky little shared plumbing, honestly.

Some of Tcl's built-in commands evaluate scripts as part of their functioning, and these commands can use exceptional return codes to enable special features. For example, the built-in Tcl commands that provide loops β€” like while, for, and foreach β€” evaluate a script that is the body of the loop. If evaluation of that loop body returns the return code TCL_BREAK or TCL_CONTINUE, the loop command can react in a way that gives the break and continue commands their documented interpretation inside loops. the loop is the one catching the vibe check πŸ”₯

Procedure invocation also involves evaluation of a script β€” the body of the procedure. And procedure invocation gives special treatment when evaluation of the procedure body returns the return code TCL_RETURN. In that circumstance, the -level entry in the return options dictionary gets decremented.

  • If, after decrementing, the value of the -level entry is 0, then the value of the -code entry becomes the return code of the procedure.
  • If, after decrementing, the value of the -level entry is greater than zero, then the return code of the procedure is TCL_RETURN.

If the procedure invocation happened during the evaluation of the body of another procedure, the process repeats itself up the call stack, decrementing the value of the -level entry at each level, so that the code will be the return code of the current command level levels up the call stack. The source command performs the same handling of the TCL_RETURN return code β€” which explains the similarity between a return invocation during a source and a return invocation within a procedure. ✨ mystery solved bestie ✨

And here's the bit that ties it all together: the return code of the return command itself is what triggers this special handling by procedure invocation.

  • If return is provided the option -level 0, then the return code of the return command itself will be the value code of the -code option (or TCL_OK by default).
  • Any other value for the -level option (including the default value of 1) will cause the return code of the return command itself to be TCL_RETURN, triggering a return from the enclosing procedure.

periodt. πŸ’…

✨ EXAMPLES ✨

Okay bestie, this is the fun part β€” actual code you can steal. πŸ’–

First up, the simplest lil guy: using return to bounce out of a procedure and interrupt the procedure body mid-sentence. It's giving "we're done here, bye."

proc printOneLine {} {
    puts "line 1"    ;# This line will be printed.
    return
    puts "line 2"    ;# This line will not be printed.
}

Next: using return to set the value your procedure hands back. She's a giver. πŸŽ€

proc returnX {} {return X}
puts [returnX]    ;# prints "X"

Next, a more complete example, using return -code error to report invalid arguments. Like, if you pass her something unhinged, she WILL tell you.

proc factorial {n} {
    if {![string is integer $n] || ($n < 0)} {
        return -code error \
                "expected non-negative integer,\
                but got \"$n\""
    }
    if {$n < 2} {
        return 1
    }
    set factor [factorial [expr {$n - 1}]]
    set product [expr {$n * $factor}]
    return $product
}

Next, a procedure replacement for break. Homemade. Artisanal. 🧚

proc myBreak {} {
    return -code break
}

With the -level 0 option, return itself can serve as a replacement for break, with the help of interp alias. No cap, she's versatile like that. πŸ’…

interp alias {} Break {} return -level 0 -code break

An example of using catch and return -options to re-raise a caught error β€” catch it, clean up your mess, then yeet it back out exactly how you found it:

proc doSomething {} {
    set resource [allocate]
    catch {
        # Long script of operations
        # that might raise an error
    } result options
    deallocate $resource
    return -options $options $result
}

Finally an example of advanced use of the return options to create a procedure replacement for return itself. Yes, she's replacing herself. Main character energy, honestly. πŸ‘‘

proc myReturn {args} {
    set result ""
    if {[llength $args] % 2} {
        set result [lindex $args end]
        set args [lrange $args 0 end-1]
    }
    set options [dict merge {-level 1} $args]
    dict incr options -level
    return -options $options $result
}

πŸ‘€ SEE ALSO πŸ‘€

Go read these next, they're all in the same friend group: break(3tcl), catch(3tcl), continue(3tcl), dict(3tcl), error(3tcl), errorCode(3tcl), errorInfo(3tcl), proc(3tcl), source(3tcl), throw(3tcl), try(3tcl)


πŸ”‘ KEYWORDS πŸ”‘

break, catch, continue, error, exception, procedure, result, return


And that's the whole page, bestie! 🫢 This one's from Tcl 8.5, and it's the return(3tcl) manual page β€” so if you ever need her again, you know exactly where to find her. Go forth and return responsibly. πŸ’–βœ¨

πŸ“¦ where this page comes from