Purpose: [??] Hmm. Let's set the ball rolling... ---- A simple way to get a read-only global variable is to use a suitable trace: set ROglobal someValue trace variable ROglobal w "[list set ::ROglobal $ROglobal];error read-only;#" Notice the time- and hair-saving tricks: 1. Using [[list]] to construct a ''guaranteed'' safe command for later execution. 1. Using colon notation to force a reference to a global variable, whatever the context. 1. Inserting the global name of the variable in the trace command, instead of working with its local referent. 1. Using a trailing ";#" to trim the undesirable extra arguments from the trace command. '''DKF''' This is great code, but hard to find if you're thinking of a constant or constants or C's #define. (There! Now the Wiki search will find this page!) - RWT ---- Neat... I remember having had trouble when the trace was set on '''::var''', yet usage was based on '''global var''', or the other way around. Does anyone know what the exact behavior is when mixing these two approaches? '''JCG''' The thing to remember is that traces are always called in the context of the operation that is performing the read, write or unset, and the standard parameters passed in from the Tcl system refer to the way in which the variable was referred to ''in that context''. Whenever you are setting up a trace on a variable that exists independently of the stack (i.e. a global or namespace variable - the two are really the same thing anyway) then it is much less hassle to pass a fixed name for that variable in as one of your own parameters to the trace (you can sort-of form closures in Tcl using appropriate scripts, and some of the more cunning lambda-evaluation schemes I've seen have been based on this.) The only time this is not going to work (in standard Tcl) is when you are dealing with variables that only exist on the call-stack - procedure locals. With these, you have to carefully build an [[upvar]] reference to the variable and then use that name to figure out what is going on. This is not easy, but it has been adequately covered in many books, like [BOOK Tcl and the Tk Toolkit] which is where I learnt all this stuff (if I can remember that far back!) '''DKF''' ---- Donal, you might be talking about the following: trace variable ::var w monitor proc monitor {name args} { upvar $name value puts "'[info level 1]' changes '$name' to '$value'" } ### Change 1 proc change1 {} { set ::var bar } change1 ### Change 2 proc change2 {} { global var set var foo } change2 ### Change 3 proc change3 {} { upvar var local set local bar } change3 The above code produces the following output: 'change1' changes '::var' to 'bar' 'change2' changes 'var' to 'foo' 'change3' changes 'local' to 'bar' The problem is that there is no way of knowing which variable is actually being modified, all one has access to is its alias name as used in the ''set'' command which trigers the trace, i.e., in ''monitor'' the parameter ''name'' is equal to ''local'', how do you resolve that name back to the original ''::var'' variable? -- JC There is ''no'' standard mechanism for doing this. So I instead pass a globally-valid (fully-quantified) variable name as part of the trace script that I supply. Like that, I don't care about how the variable was accessed since I can always access it myself using a clearly defined route. And, because of the restrictions involved with the use of variables and Tk, I find sticking to only putting traces on globally-valid names to not be a problem. Note that [[namespace current]] is very useful when you want to put in code to automatically figure out what the globally-valid variable name actually is. '''DKF''' ---- If you want to set up a trace on an expression, then you implement this using the style advocated in the following example: Suppose you want to have a trace on a pair of variables such that some callback (which I'll imaginatively describe as ''callback'' here) is called whenever some complex set of conditions (either ''$VAR_I>0 && $VAR_B=="true"'' or ''$VAR_I>=10 && $VAR_B=="false"'') then you would do it like this: trace variable VAR_I w doTest trace variable VAR_B w doTest proc doTest args { upvar #0 VAR_I i VAR_B b if { ($i>0 && [string equal $b "true"]) || ($i>=10 && [string equal $b "false"]) } then { upvar #0 callback } } You can't extend this to a general watch of an expression, since an expression can include a call to a general command, and solving exactly what variables a general command accesses is tough. Even if we restricted ourselves to procedures, we would still need to solve the [Halting Problem] (a famously insoluble thing in Computer Science, where there is a ''proof'' of insolubility...) Since you can usually tell straight off what variables in an expression actually matter, you can shortcut all that stuff and just tell Tcl exactly what to watch yourself... '''DKF''' ---- Come on people, this page isn't just "Ask Dr. Donal" - fill in more of this stuff yourselves! '''DKF''' ---- See also [trace]. ---- [Category Tutorial]