Main Content

Passing back multiple results in Tcl - upvar and uplevel

Archive - Originally posted on "The Horse's Mouth" - 2011-09-01 17:13:55 - Graham Ellis

What's the effect of having a meal? There are many. "You're not hungry any more" is the obvious answer, but also "there's washing up to do", "there's a gap on the shelves" and so on.

And yet when programming with an assignment statement (or a set command in Tcl - I am running a Tcl course this week) there's just a single returned value that is saved to a resut variable.

In Tcl, multiple values could be returned in a list just as in other languages they could be returned in a list / array / hash / tuple / object / dictionary, but Tcl also lets you pass in the names of extra return variables to your proc and then access them within a proc via upvar, or even reach out to variables in the calling code via uplevel.

How does this work?

I wrote an example on yesterday's course - source code [here] - where I was working out how many widgets I could buy for a certain amount of money. The return value was to be the quantity, but I wanted to set a subsiduary result too from which the calling code could ascertain how much money was left. The call:

  set stocklevel [buy $each $cashinhand cashleft]
  puts "You can buy $stocklevel and you will have $cashleft still to spend"


Note that the target variable for the seond return has no $ in front of it. You're passing in the name of the variable, so that you can declare your proc as follows:

  proc buy {unit money_in change} {
      upvar $change payback


which says "The variable who's NAME is passed in in the "change" variable from the calling code is to have another name - an alias - of "payback" in the code of the proc. So that when I set payback:

  set payback [format %.2f [expr $money_in - $unit * $afford]]

I am really setting the variable named in $change in the calling code ... and that's the variable in that calling code called "cashleft".




There's an alternative - which I'm mentioning here for completeness - the uplevel command. With uplevel, you can request that a piece of code within a proc is performed in the variable namespace of the parent. So another way of returning the second value is to set it directly in the "calling scope":

  uplevel "set cashleft $payback"

Under most circumstances, upvar is going to give you better flexibility, though, as the calling code will be able to specify a different variable name each time the proc is called rather than being hardwired, and harder to maintain. There is, however, a new full example of uplevel [here] on the web site.