Updated 2013-03-31 21:39:55 by RLE

Richard Suchenwirth 2006-06-05 - "Environment" is understood here as a set of variables and their values, like many operating systems make available to processes (in Tcl, the env variable mirrors that).

There may be situations when you want to provide a local environment from one proc A to another, B, without having to build a long argument list, nor having to use global variables. Other languages with lexical scoping, like Lisp, have that as built-in feature, but here's a simple way how to do that in Tcl: a {name value name value...} list is wrapped, passed to the called function, which in turn unwraps it. You have to specify which variables shall go in, that's why I call it "controlled environment" :^)

Usage ("name" being a freely choosable variable name):
 env name add var... ;# in the caller, who calls the callee with $name
 env name get        ;# in the callee

Only scalar variables can be wrapped. The callee gets copies of the variables which he can change at will, without influencing the "originals" in the caller. Both features mirror those of OS environment variables.
 proc env {_name mode args} {
    upvar 1 $_name var
    switch -- $mode {
        add {foreach n $args {lappend var $n [uplevel 1 set $n]}}
        get {foreach {n val} $var {uplevel 1 [list set $n $val]}}
        default {error "bad mode $mode, use add or get"}
    }
 }
#-- Testing (the example should leave no question open):
 proc caller arg {
    set foo 42
    set bar grill
    env e add arg foo bar
    callee 123 $e
 }
 proc callee {x e} {
    env e get
    foreach name [info vars] {
        lappend res $name=[set $name]
    }
    set res
 }
 puts [caller hello!]

which returns:
 x=123 {e=arg hello! foo 42 bar grill} foo=42 bar=grill arg=hello!

See also closures.

NEM: In particular, see Simple Closures and Objects, which does a similar thing, but using dict with to restore the environment in the new scope.